Cálculos rápidos de trigonometria
Sua tarefa é criar um programa que possa calcular o seno, o cosseno e a tangente de um ângulo em graus.
Regras
- Não há funções trigonométricas embutidas (nem mesmo secantes, cossecantes e cotangentes, se o seu idioma as possuir).
- Você pode usar tabelas de pesquisa, mas seu tamanho total não deve exceder 3000 membros (para todas as três operações juntas). Por favor, faça com que ele leia as tabelas de um arquivo (por exemplo
trig.lookup
) para que elas não confundam o código. - Sem acesso à rede.
- Você deve arredondar corretamente sua saída, conforme explicado abaixo. Não use piso ou teto.
- Você pode usar qualquer método para calcular os valores, por exemplo , frações continuadas , desde que esteja correto para 7 números significativos.
- Seu código deve ser capaz de cronometrar a si próprio. Exclua do seu tempo as operações de E / S do arquivo - apenas calcule o tempo das funções que executam o trig e qualquer arredondamento.
- Eu devo ser capaz de executar seu código. Publique um link em um compilador / intérprete disponível gratuitamente e forneça as instruções necessárias para compilar / executar o código (por exemplo, quais opções passar para o GCC).
- Aplicam-se brechas padrão .
Formato de entrada
- Leia de um arquivo chamado, a
trig.in
menos que seu idioma não suporte E / S de arquivo. - Os ângulos estão entre 0 e 360, inclusive.
- A entrada consistirá em ângulos com dez algarismos significativos em dígitos decimais, separados por novas linhas. Por exemplo:
90.00000000
74.54390000
175.5000000
Formato de saída
- Para cada ângulo fornecido, você deve emitir seu seno, cosseno e tangente para 7 algarismos significativos, separados por espaços, em uma única linha. Use "notação científica", por exemplo,
1.745329E-5
paratan 0.001
ou1.000000E+0
parasin 90
. - Denote infinito ou NaN por
n
, por exemplo, a saída para90.00000000
deve ser1.000000 0.000000 n
. - Se a entrada tiver três ângulos separados por novas linhas, sua saída deverá consistir em três linhas, cada uma contendo o seno, o cosseno e a tangente.
- Você não pode produzir mais nada.
- Saída para um arquivo chamado, a
trig.out
menos que seu idioma não suporte E / S de arquivo.
Pontuação
- código mais rápido . O desafio é escrever um programa que calcule esses três valores o mais rápido possível. O tempo mais rápido vence.
- Todos receberão a mesma entrada de teste de muitos ângulos.
- Os horários serão gravados na minha máquina.
- Sua pontuação é a média de três corridas na mesma entrada (você não pode salvar nada entre as corridas, obviamente).
- Tempo de compilação não incluído. Esse desafio é mais sobre o método usado que a linguagem. (Se alguém pudesse me indicar como excluiria o tempo de compilação para linguagens como Java, ficaria muito grato)
- Minha máquina é uma instalação do Ubuntu 14.04. As estatísticas do processador estão em Pastebin (obtidas executando
cat /proc/cpuinfo
). - Vou editar o seu tempo na sua resposta quando o testar.
math
fastest-code
trigonometry
Geobits
fonte
fonte
sin
,cos
etan
está em uma nova linha. Preciso alterá-lo para gerar as respostas em uma única linha?Respostas:
Fortran 90
I empregar o CORDIC método com uma matriz pré-tabulados de 60 valores arctan (ver artigo Wiki para detalhes sobre o porquê de que é necessário).
Esse código requer que um arquivo,
trig.in
com todos os valores nas novas linhas seja armazenado na mesma pasta que o executável do Fortran. Compilar isso é,onde
file
está o nome do arquivo que você fornecer (provavelmenteSinCosTan.f90
seria mais fácil, embora não seja necessário corresponder o nome do programa e o nome do arquivo). Se você possui o compilador Intel, eu recomendo usarcomo o
-xHost
(que não existe para o gfortran) fornece otimizações de nível superior disponíveis para o seu processador.Minhas execuções de teste me deram cerca de 10 microssegundos por cálculo ao testar 1000 ângulos aleatórios usando gfortran 4.4 (4.7 ou 4.8 está disponível nos repositórios Ubuntu) e cerca de 9,5 microssegundos usando ifort 12.1. Testar apenas 10 ângulos aleatórios resultará em um tempo indeterminável usando as rotinas do Fortran, pois a rotina de tempo é precisa até o milissegundo e a matemática simples diz que deve levar 0,100 milissegundos para executar todos os 10 números.
EDIT Aparentemente, eu estava com o tempo de IO, o que (a) tornava o tempo mais longo que o necessário e (b) é contrário ao item 6. Atualizei o código para refletir isso. Também descobri que o uso de um
kind=8
número inteiro com a sub-rotina intrínsecasystem_clock
fornece precisão de microssegundos.Com este código atualizado, agora estou computando cada conjunto de valores das funções trigonométricas em cerca de 0,3 microssegundos (os dígitos significativos no final variam de execução para execução, mas está pairando consistentemente perto de 0,31 nós), uma redução significativa em relação à anterior iteração que cronometrou o IO.
fonte
Python 2.7.x ou Java (faça a sua escolha)
Um intérprete Python gratuito pode ser baixado aqui .
Um interpretador Java gratuito pode ser baixado aqui .
O programa pode receber entradas de um arquivo chamado
trig.in
localizado no mesmo diretório que o arquivo do programa. A entrada é separada por novas linhas.Eu originalmente fiz isso em python porque - bem, eu amo python. Mas, como também quero tentar vencer, reescrevi em java depois ...
Versão Python: Eu tenho cerca de 21µs por execução no meu computador. Eu obtive cerca de 32µs ao executá-lo no IDEone .
Versão Java: Recebo cerca de 0,4 µs por execução no meu computador e 1,8 µs no IDEone .
Especificações do computador:
Teste:
sin
,cos
etan
todos os ângulos de entrada.A entrada de teste usada para ambos é a seguinte:
Sobre o código:
A premissa deste programa era estimar
sin
ecos
usar seus polinômios de Taylor com 14 termos, que foi o que eu calculei necessário para ter uma estimativa de erro menor que 1e-8. No entanto, eu achei que era mais rápido calcular dosin
quecos
, então decidi calcularcos
usandocos=sqrt(1-sin^2)
Versão Python:
Versão Java:
fonte
cos
cálculo é exagero, eu tinha acabado de fazersin(x+90degrees)
sin
como uma função e uma variável. Eu pensei que seria mais rápido não ter que passar algo pelasin()
segunda vez, mas vou comparar os dois para ver se esse é realmente o caso. Sua impressão de que acopySign()
função é mais lenta do que adicionar coisas como na minhasin()
função?Oitava (ou Matlab) & C
Um pouco de um processo de compilação complicado, mas meio que uma nova abordagem e os resultados foram encorajadores.
A abordagem é gerar polinômios quadráticos aproximados para cada grau. Portanto, grau = [0, 1), grau = [1, 2), ..., degree = [359, 360) terão, cada um, um polinômio diferente.
Octave - parte do edifício
Oitava está disponível publicamente - Google
download octave
.Isso determina o polinômio quadrático de melhor ajuste para cada grau.
Salvar como
build-fast-trig.m
:C - parte do edifício
Isso converte o dobro no formato de texto para o formato binário nativo no seu sistema.
Salvar como
build-fast-trig.c
:Compilar:
Gerando o arquivo de coeficientes
Corre:
Agora, temos
qcoeffs.dat
como arquivo de dados a ser usado no programa atual.C - parte fast-trig
Salvar como
fast-trig.c
:Compilar:
Corre:
Ele lerá
trig.in
, salvarátrig.out
e imprimirá para consolar o tempo decorrido com precisão de milissegundos.Dependendo dos métodos de teste utilizados, pode falhar em determinadas entradas, por exemplo:
A saída correta deve ser
0.000000e+00 1.000000e+00 0.000000e+00
. Se os resultados forem validados usando cadeias, a entrada falhará, se eles forem validados usando erro absoluto, por exemplofabs(actual - result) < 1e-06
, a entrada será aprovada.O erro absoluto máximo para
sin
ecos
foi ≤ 3e-07. Poistan
, como o resultado não está limitado a ± 1 e você pode dividir um número relativamente grande por um número relativamente pequeno, o erro absoluto pode ser maior. De -1 ≤ tan (x) ≤ +1, o erro absoluto máximo foi ≤ 4e-07. Para tan (x)> 1 e tan (x) <-1, o erro relativo máximo , por exemplo,fabs((actual - result) / actual)
era geralmente <1e-06 até chegar na área de (90 ± 5) ou (270 ± 5) graus, então o erro piora.Nos testes, o tempo médio por entrada única foi de (1,053 ± 0,007) µs, que na minha máquina foi cerca de 0,070 µs mais rápido que o nativo
sin
ecos
,tan
sendo definido da mesma maneira.fonte
Cobra
Compile com
cobra filename -turbo
Testes: AMD FX6300 a 5.1GHz
O teste de 360 * 10000 usado pela resposta C é executado em 365ms (vs 190ms)
O teste de 4 entradas usado pelas respostas Python e Java é executado em 0,32µs (vs 30µs, 3µs)
O teste de ângulo aleatório de 1000 usado pela resposta de Fortran é executado a 100ns por ângulo (vs 10µs)
fonte
C
Aqui está a minha tentativa. Funciona assim:
Construa uma tabela de todos os valores de sin (x) de 0 a 450 graus. Equivalentemente, são todos os valores de cos (x) de -90 a 360 graus. Com 2926 elementos, há espaço suficiente para um valor a cada 1 / 6,5 graus. A unidade de programa é, portanto, de 1/6,5 graus e existem 585 unidades em um quarto de volta.
Converta graus de entrada em unidades de programa (multiplique por
6.5==110.1 binary.
) Encontre os valores mais próximos para sin e cos da tabela. depois converta a parte restante da entrada (dx) em radianos.aplique a fórmula
sin(x+dx) == sin x +(d(sin x)/dx)*dx.
observe que,(d(sin x)/dx)==cos x,
mas somente se usarmos radianos.infelizmente, isso não é preciso o suficiente por si só; portanto, é necessário outro termo, com base na próxima derivada.
d2(sin x)/dx2 == -sin x.
Isso precisa ser multiplicado pordx*dx/2
(não se sabe de onde vem o fator 2, mas funciona).Siga o procedimento análogo para
cos x
, depois calculetan x == sin x / cos x
.Código
Existem cerca de 17 operações de ponto flutuante aqui. Isso pode ser melhorado um pouco. O programa contém criação de tabela e saída de teste usando as funções trigonométricas nativas, mas o algoritmo não. Vou adicionar tempo e editar para atender aos requisitos de E / S mais tarde (espero que este fim de semana). Corresponde à saída da função nativa, exceto por valores muito pequenos de sin x e cos x, que devem ser melhoráveis que a saída da função nativa com alguns ajustes.
fonte