Cálculo de disparo rápido

16

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.inmenos 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-5para tan 0.001ou 1.000000E+0para sin 90.
  • Denote infinito ou NaN por n, por exemplo, a saída para 90.00000000deve ser 1.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.outmenos que seu idioma não suporte E / S de arquivo.

Pontuação

  • . 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.
Geobits
fonte
A saída precisa estar em uma única linha? Parece tão bonito quando formatado com uma tecla enter ... Além disso, há uma data específica na qual um vencedor é escolhido?
Efraim
@ Efraim, o que você quer dizer com formatado com uma tecla enter? não, não há uma data específica. Eu realmente preciso testar todas essas soluções, mas ainda não fiz a entrada de teste; (#
@professorfish - veja a saída na minha resposta. Cada sin, cose tanestá em uma nova linha. Preciso alterá-lo para gerar as respostas em uma única linha?
Efraim
2
@Ephraim O formato de saída realmente não importa (o que não é o código-golfe), desde que ele emite os cos sin e tan para todos os ângulos e eles são separados
11
Devemos cronometrar apenas os cálculos trigonométricos ou incluir o io no tempo?
gggg

Respostas:

6

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.incom todos os valores nas novas linhas seja armazenado na mesma pasta que o executável do Fortran. Compilar isso é,

gfortran -O3 -o file file.f90

onde fileestá o nome do arquivo que você fornecer (provavelmente SinCosTan.f90seria 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 usar

ifort -O3 -xHost -o file file.f90

como 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=8número inteiro com a sub-rotina intrínseca system_clockfornece 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.


program SinCosTan
   implicit none
   integer, parameter :: real64 = selected_real_kind(15,307)
   real(real64), parameter :: PI  = 3.1415926535897932384626433832795028842
   real(real64), parameter :: TAU = 6.2831853071795864769252867665590057684
   real(real64), parameter :: half = 0.500000000000000000000_real64
   real(real64), allocatable :: trigs(:,:), angles(:)
   real(real64) :: time(2), times, b
   character(len=12) :: tout
   integer :: i,j,ierr,amax
   integer(kind=8) :: cnt(2)

   open(unit=10,file='trig.out',status='replace')
   open(unit=12,file='CodeGolf/trig.in',status='old')
! check to see how many angles there are
   i=0
   do
      read(12,*,iostat=ierr) b
      if(ierr/=0) exit
      i=i+1
   enddo !- 
   print '(a,i0,a)',"There are ",i," angles"
   amax = i

! allocate array
   allocate(trigs(3,amax),angles(amax))

! rewind the file then read the angles into the array
   rewind(12)
   do i=1,amax
      read(12,*) angles(i)
   enddo !- i

! compute trig functions & time it
   times = 0.0_real64
   call system_clock(cnt(1)) ! <-- system_clock with an 8-bit INT can time to us
   do i=1,amax
      call CORDIC(angles(i), trigs(:,i), 40)
   enddo !- i
   call system_clock(cnt(2))
   times = times + (cnt(2) - cnt(1))

! write the angles to the file
   do i=1,amax
      do j=1,3
         if(trigs(j,i) > 1d100) then
            write(tout,'(a1)') 'n'
         elseif(abs(trigs(j,i)) > 1.0) then
            write(tout,'(f10.6)') trigs(j,i)
         elseif(abs(trigs(j,i)) < 0.1) then
            write(tout,'(f10.8)') trigs(j,i)
         else
            write(tout,'(f9.7)') trigs(j,i)
         endif
         write(10,'(a)',advance='no') tout
      enddo !- j
      write(10,*)" "
   enddo !- i

   print *,"computation took",times/real(i,real64),"us per angle"
   close(10); close(12)
 contains
   !> @brief compute sine/cosine/tangent
   subroutine CORDIC(a,t,n)
     real(real64), intent(in) :: a
     real(real64), intent(inout) :: t(3)
     integer, intent(in) :: n
! local variables
     real(real64), parameter :: deg2rad = 1.745329252e-2
     real(real64), parameter :: angles(60) = &
       [ 7.8539816339744830962e-01_real64, 4.6364760900080611621e-01_real64, &
         2.4497866312686415417e-01_real64, 1.2435499454676143503e-01_real64, &
         6.2418809995957348474e-02_real64, 3.1239833430268276254e-02_real64, &
         1.5623728620476830803e-02_real64, 7.8123410601011112965e-03_real64, &
         3.9062301319669718276e-03_real64, 1.9531225164788186851e-03_real64, &
         9.7656218955931943040e-04_real64, 4.8828121119489827547e-04_real64, &
         2.4414062014936176402e-04_real64, 1.2207031189367020424e-04_real64, &
         6.1035156174208775022e-05_real64, 3.0517578115526096862e-05_real64, &
         1.5258789061315762107e-05_real64, 7.6293945311019702634e-06_real64, &
         3.8146972656064962829e-06_real64, 1.9073486328101870354e-06_real64, &
         9.5367431640596087942e-07_real64, 4.7683715820308885993e-07_real64, &
         2.3841857910155798249e-07_real64, 1.1920928955078068531e-07_real64, &
         5.9604644775390554414e-08_real64, 2.9802322387695303677e-08_real64, &
         1.4901161193847655147e-08_real64, 7.4505805969238279871e-09_real64, &
         3.7252902984619140453e-09_real64, 1.8626451492309570291e-09_real64, &
         9.3132257461547851536e-10_real64, 4.6566128730773925778e-10_real64, &
         2.3283064365386962890e-10_real64, 1.1641532182693481445e-10_real64, &
         5.8207660913467407226e-11_real64, 2.9103830456733703613e-11_real64, &
         1.4551915228366851807e-11_real64, 7.2759576141834259033e-12_real64, &
         3.6379788070917129517e-12_real64, 1.8189894035458564758e-12_real64, &
         9.0949470177292823792e-13_real64, 4.5474735088646411896e-13_real64, &
         2.2737367544323205948e-13_real64, 1.1368683772161602974e-13_real64, &
         5.6843418860808014870e-14_real64, 2.8421709430404007435e-14_real64, &
         1.4210854715202003717e-14_real64, 7.1054273576010018587e-15_real64, &
         3.5527136788005009294e-15_real64, 1.7763568394002504647e-15_real64, &
         8.8817841970012523234e-16_real64, 4.4408920985006261617e-16_real64, &
         2.2204460492503130808e-16_real64, 1.1102230246251565404e-16_real64, &
         5.5511151231257827021e-17_real64, 2.7755575615628913511e-17_real64, &
         1.3877787807814456755e-17_real64, 6.9388939039072283776e-18_real64, &
         3.4694469519536141888e-18_real64, 1.7347234759768070944e-18_real64]
     real(real64), parameter :: kvalues(33) = &
       [ 0.70710678118654752440e+00_real64, 0.63245553203367586640e+00_real64, &
         0.61357199107789634961e+00_real64, 0.60883391251775242102e+00_real64, &
         0.60764825625616820093e+00_real64, 0.60735177014129595905e+00_real64, &
         0.60727764409352599905e+00_real64, 0.60725911229889273006e+00_real64, &
         0.60725447933256232972e+00_real64, 0.60725332108987516334e+00_real64, &
         0.60725303152913433540e+00_real64, 0.60725295913894481363e+00_real64, &
         0.60725294104139716351e+00_real64, 0.60725293651701023413e+00_real64, &
         0.60725293538591350073e+00_real64, 0.60725293510313931731e+00_real64, &
         0.60725293503244577146e+00_real64, 0.60725293501477238499e+00_real64, &
         0.60725293501035403837e+00_real64, 0.60725293500924945172e+00_real64, &
         0.60725293500897330506e+00_real64, 0.60725293500890426839e+00_real64, &
         0.60725293500888700922e+00_real64, 0.60725293500888269443e+00_real64, &
         0.60725293500888161574e+00_real64, 0.60725293500888134606e+00_real64, &
         0.60725293500888127864e+00_real64, 0.60725293500888126179e+00_real64, &
         0.60725293500888125757e+00_real64, 0.60725293500888125652e+00_real64, &
         0.60725293500888125626e+00_real64, 0.60725293500888125619e+00_real64, &
         0.60725293500888125617e+00_real64 ]
    real(real64) :: beta, c, c2, factor, poweroftwo, s
    real(real64) :: s2, sigma, sign_factor, theta, angle
    integer :: j

! scale to radians
    beta = a*deg2rad
! ensure angle is shifted to appropriate range
    call angleShift(beta, -PI, theta)
! check for signs
    if( theta < -half*PI) then
       theta = theta + PI
       sign_factor = -1.0_real64
    else if( half*PI < theta) then
       theta = theta - PI
       sign_factor = -1.0_real64
    else
       sign_factor = +1.0_real64
    endif

! set up some initializations...    
    c = 1.0_real64
    s = 0.0_real64
    poweroftwo = 1.0_real64
    angle = angles(1)

! run for 30 iterations (should be good enough, need testing)
    do j=1,n
       sigma = merge(-1.0_real64, +1.0_real64, theta <  0.0_real64)
       factor = sigma*poweroftwo

       c2 = c - factor*s
       s2 = factor*c + s
       c = c2
       s = s2
! update remaining angle
       theta = theta - sigma*angle

       poweroftwo = poweroftwo*0.5_real64
       if(j+1 > 60) then
          angle = angle * 0.5_real64
       else
          angle = angles(j+1)
       endif
    enddo !- j

    if(n > 0) then
       c = c*Kvalues(min(n,33))
       s = s*Kvalues(min(n,33))
    endif
    c = c*sign_factor
    s = s*sign_factor

    t = [s, c, s/c]
   end subroutine CORDIC

   subroutine angleShift(alpha, beta, gamma)
     real(real64), intent(in) :: alpha, beta
     real(real64), intent(out) :: gamma
     if(alpha < beta) then
        gamma = beta - mod(beta - alpha, TAU) + TAU
     else
        gamma = beta + mod(alpha - beta, TAU) 
     endif
   end subroutine angleShift

end program SinCosTan
Kyle Kanos
fonte
2
Finalmente, alguém usou CORDIC: D
qwr
11
Eu acho que "-march = native" é a bandeira gfortran correspondente ao ifort "-xHost". Além disso, acredito que a Intel -O3 definiu um modo mais agressivo que o gfortran, para que você possa tentar o gfortran com "-O3 -fno-protect-parens -fstack-arrays" para ver se isso ajuda.
semi-extrínseco
Além disso, você está cronometrando a parte IO também, desde que você leia dentro do loop. As regras dizem especificamente que você não deve cronometrar IO. Corrigir isso deu uma aceleração bastante no meu computador: 0,37 microssegundos por valor, contra 6,94 no código postado. Além disso, o código publicado não é compilado, há uma vírgula à direita na linha 100. Há também um erro na linha 23: trigs (i) devem ser apenas trigs. Isso faz com que o código postado segfault.
semi-extrínseco
Versão aprimorada aqui: pastebin.com/freiHTfx
semi-extrínseco
Atualizar as opções do re: compilador: -march e -fno-protect-parens não fizeram nada, mas -fstack-arrays eliminou outros 0,1 microssegundos por valor. "ifort -O3 -xHost" é, notavelmente, quase duas vezes mais lento que "gfortran -O3 -fstack-arrays": 0,55 vs. 0,27
semi-extrínseco
2

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:

  • Windows 8.1, atualização 1 de 64 bits com intel core i7-3632QM - 2.2GHz)

Teste:

  • Tempo por run" é o tempo acumulado é preciso para calcular o sin, cose tantodos os ângulos de entrada.
  • A entrada de teste usada para ambos é a seguinte:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


Sobre o código:
A premissa deste programa era estimar sine cosusar 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 do sinque cos, então decidi calcular cosusandocos=sqrt(1-sin^2)

Série de pecados de Maclaurin (x) Série Maclaurin de cos (x)


Versão Python:

import math
import timeit
import sys
import os
from functools import partial

#Global Variabls
pi2 = 6.28318530718
piover2 = 1.57079632679

#Global Lists
factorialList = [1.0,
                 -6.0,
                 120.0,
                 -5040.0,
                 362880.0,
                 -39916800.0,
                 6227020800.0,
                 -1307674368000.0,
                 355687428096000.0,
                 -121645100408832000.0,
                 51090942171709440000.0,
                 -25852016738884976640000.0,
                 15511210043330985984000000.0,
                 -10888869450418352160768000000.0,
                 8841761993739701954543616000000.0]

#simplifies angles and converts them to radians
def torad(x):  
    rev = float(x)/360.0
    if (rev>1) or (rev<0):
        return (rev - math.floor(rev))*pi2
    return rev*pi2

def sinyield(x):
    squared = x*x
    for n in factorialList:
        yield x/n
        x*=squared

def tanfastdivide(sin, cos):
    if (cos == 0):
        return "infinity"  
    return sin/cos

#start calculating sin cos and tan
def calcyield(outList):
    for angle in outList[0]:
        angle = torad(angle)
        sin = round(math.fsum(sinyield(angle)), 7)
        cos=math.copysign(math.sqrt(1-(sin*sin)),(piover2-angle))
        yield sin
        yield cos
        yield tanfastdivide(sin, cos) #tan

def calculations(IOList):
    calcyieldgen = calcyield(IOList)
    for angle in IOList[0]:
        IOList[1].append(next(calcyieldgen))
        IOList[2].append(next(calcyieldgen))
        IOList[3].append(next(calcyieldgen))
    return IOList

#Begin input from file
def ImportFile():
    try:
        infile = open("trig.in", "r")
    except:
        infile = sys.stdin
    inList = [[], [], [], []]

    #take input from file
    for line in infile:
        inList[0].extend([float(line)])
    return inList

#Begin output to file
def OutputResults(outList):
    try:
        outfile = open("trig.out", "w")
        PrintOutput(outList, outfile)    
    except:
        print 'Failed to write to file. Printing to stdout instead...'
    finally:
        PrintOutput(outList, sys.stdout)

def PrintOutput(outList, outfile):
    #outList[0][i] holds original angles
    #outList[1][i] holds sin values
    #outList[2][i] holds cos values
    #outList[3][i] holds tan values
    outfile.write('-----------------------------------------------------\n')
    outfile.write('                    TRIG RESULTS                     \n')
    outfile.write('-----------------------------------------------------\n')
    for i in range(len(outList[0])):
        if (i):
            outfile.write('\n')
        outfile.write("For angle: ")
        outfile.write(str(outList[0][i]))
        outfile.write('\n    ')
        outfile.write("Sin: ")
        outfile.write(str('%.7E' % float(outList[1][i])))
        outfile.write('\n    ')
        outfile.write("Cos: ")
        outfile.write(str('%.7E' % float(outList[2][i])))
        outfile.write('\n    ')
        outfile.write("Tan: ")
        outfile.write(str('%.7E' % float(outList[3][i])))


#Run the Program first
inList = ImportFile()
OutputResults(calculations(inList))

#Begin Runtime estimation
def timeTest(IOList):
    for output in calcyield(IOList):
        pass
def baselined(inList):
    for x in inList:
        pass

totime = timeit.Timer(partial(timeTest, inList))
baseline = timeit.Timer(partial(baselined, inList))
print '\n-----------------------------------------------------'
print '                    TIME RESULTS:                    '
print '-----------------------------------------------------'
OverheadwithCalcTime =  min(totime.repeat(repeat=10, number=10000))
Overhead = min(baseline.repeat(repeat=1, number=10000))
estimatedCalcTime = (OverheadwithCalcTime - Overhead)
estimatedTimePerAngle = estimatedCalcTime/len(inList)
estimatedTimePerCalc = estimatedTimePerAngle/3
print ' Estimated CalcTime+Overhead:.....', '%.10f' % (OverheadwithCalcTime*100), 'µsec'
print ' Estimated Overhead Time:..........', '%.10f' % (Overhead*100), 'µsec'
print ''
print ' Estimated CalcTime/Run:..........', '%.10f' % (estimatedCalcTime*100), 'µsec'
print ' Estimated CalcTime/Angle:.........', '%.10f' % (estimatedTimePerAngle*100), 'µsec'
print ' Estimated CalcTime/Cacluation:....', '%.10f' % (estimatedTimePerCalc*100), 'µsec'
print '-----------------------------------------------------'
print "                   COOL, IT WORKED!                  "
print '-----------------------------------------------------'


Versão Java:

import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Scanner;
import java.lang.Math;

class Trig {
   /**
    *Global Variables
    **/
    static final double pi2 = 6.28318530718;
    public long totalTime = 0L;
    static final double piover2 = 1.57079632679;
    static final double plusinfty = Double.POSITIVE_INFINITY;
    static final double minusinfty = Double.NEGATIVE_INFINITY;
    static final double factoriallist[] =
                             new double[]{
                         -6.0,120.0,-5040.0,362880.0,-39916800.0,
                         6227020800.0,-1307674368000.0,355687428096000.0,
                        -121645100408832000.0,51090942171709440000.0,
                        -25852016738884976640000.0,
                         15511210043330985984000000.0,
                        -10888869450418352160768000000.0,
                         8841761993739701954543616000000.0
                         };
//Begin Program
    public static void main(String[] args) {
        Trig mytrig = new Trig();
        double[] input = mytrig.getInput();
        double[][] output = mytrig.calculate(input);
        mytrig.OutputResults(output);
        Trig testIt = new Trig();
        testIt.timeIt(input);
    }

//Begin Timing
    public void timeIt(double[] input) {
        double overhead = 0L;
        totalTime = 0L;

        for (int i = 0; i < 1000001; i++) {
            calculate(input);
            if (i == 0) {
                overhead = totalTime;
                totalTime = 0L;
            }
        }
        double averageTime = ((Double.valueOf(totalTime-overhead))/1000000.0);
        double perAngleTime = averageTime/input.length;
        double perOpperationTime = perAngleTime/3;
        NumberFormat formatter = new DecimalFormat("0000.0000");
        System.out.println("\n-----------------------------------------------------");
        System.out.println("                    TIME RESULTS:                    ");
        System.out.println("-----------------------------------------------------");
        System.out.println("Average Total  Runtime:.......... " + formatter.format(averageTime) + " nsec");
        System.out.println("                                = " + formatter.format(averageTime/1000) + " usec\n");
        System.out.println("Average Runtime Per Angle:....... " + formatter.format(perAngleTime) + " nsec");
        System.out.println("                                = " + formatter.format(perAngleTime/1000) + " usec\n");
        System.out.println("Average Runtime Per Opperation:.. " + formatter.format(perOpperationTime) + " nsec");
        System.out.println("                                = " + formatter.format(perOpperationTime/1000) + " usec");
    }

//Begin Input
    double[] getInput() {
        Scanner in;
        ArrayList<Double> input = new ArrayList<Double>();
        try {
            in = new Scanner(new File("trig.in"));
        } catch (FileNotFoundException e) {
            new FileNotFoundException("Failed to read from file. Reading from stdin instead...").printStackTrace();
            in= new Scanner(System.in);
        }
        while (in.hasNextLine()) {
            Double toadd = Double.parseDouble(in.nextLine());
            input.add(toadd);   
        }
        in.close();
        double[] returnable = new double[input.size()];
        for(int i = 0; i < input.size(); i++) {returnable[i] = input.get(i);}
        return returnable;
    }

//Begin OutputStream Choice
    void OutputResults(double[][] outList) {
        PrintStream out;
        try {
            out = new PrintStream("trig.out");
            PrintOutput(outList, out);
            PrintOutput(outList, System.out);
        } catch (FileNotFoundException e) {
            new FileNotFoundException("Failed to write to file. Printing to stdout instead...").printStackTrace();
            PrintOutput(outList, System.out);
        }
    }

//Begin Output
    static void PrintOutput(double[][] outList, PrintStream out) {
        /**
         *outList[0][i] holds original angles
         *outList[1][i] holds sin values
         *outList[2][i] holds cos values
         *outList[3][i] holds tan values
         */
        NumberFormat formatter = new DecimalFormat("0.0000000E0");
        out.println("-----------------------------------------------------");
        out.println("                    TRIG RESULTS                     ");
        out.println("-----------------------------------------------------");
        for (int i=0; i<outList[0].length; i++) {
            out.println("For Angle: " + outList[0][i]);

            out.println("      sin: " + formatter.format(outList[1][i]));
            out.println("      cos: " + formatter.format(outList[2][i]));
            if (Double.valueOf(outList[3][i]).isInfinite() || Double.valueOf(outList[3][i]).isNaN()) {
                out.println("      tan: " + outList[3][i]);
            }
            else out.println("      tan: " + formatter.format(outList[3][i]));
        }
        if (out != System.out) out.close();
    }

//Begin Calculations
    double[][] calculate(double[] IOList) {
        double[][] outList = new double[4][IOList.length];
        double sin;
        double cos;
        double tan;
        double rads;
        int i = 0;
        long calctime = 0L;
        long startTime;
        long endTime;
        for (double input : IOList) {
            startTime = System.nanoTime();
            rads = toRad(input);
            sin=sin(rads);
            cos = ((piover2-rads)>=0) ? Math.sqrt((1.0-(sin*sin))) : -Math.sqrt((1.0-(sin*sin)));
            tan = (cos!=0.0d) ? sin/cos : ((sin>0) ? plusinfty : minusinfty);
            endTime = System.nanoTime();
            calctime = calctime + endTime - startTime;
            outList[0][i] = input;
            outList[1][i] = sin;
            outList[2][i] = cos;
            outList[3][i] = tan;
            i++;
        }
        totalTime = totalTime + calctime;
        return outList;
    }

//Convert Degrees to Radians
    double toRad(double deg) {
        double rev = deg/360.0;
        return (rev>1 || rev<0) ? Math.abs(rev - ((int)rev))*pi2 : rev*pi2;
    }

//Get sin
    double sin(double angle) {
        double sqr = angle*angle;
        double value = angle;
        for (double fct : factoriallist) {
            value += ((angle*=sqr)/fct);
        }
        return ((long)((value + Math.copySign(0.0000000005d, value))*10000000.0))/10000000.0;
    }   
}
Efraim
fonte
Seus cossenos estão errados para 180 <x <360, e o programa falhar completamente em 270.
Οurous
@ Ourur - eu o modifiquei, por isso deve funcionar agora nos dois idiomas.
Efraim
Você coscálculo é exagero, eu tinha acabado de fazersin(x+90degrees)
Skizz
@ Skizz - No meu programa, eu uso a palavra sincomo uma função e uma variável. Eu pensei que seria mais rápido não ter que passar algo pela sin()segunda vez, mas vou comparar os dois para ver se esse é realmente o caso. Sua impressão de que a copySign()função é mais lenta do que adicionar coisas como na minha sin()função?
Efraim
Ah, vejo que você está cometendo pecado e porque ao mesmo tempo. Meu comentário só seria realmente válido se você estivesse cometendo pecado ou porque.
Skizz
0

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:

format long;
for d = 0:359
    x = (d-1):0.1:(d+1);
    y = sin(x / 360 * 2 * pi);
    polyfit(x, y, 2)
endfor

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:

#include <stdio.h>

int main()
{
    double d[3];

    while (scanf("%lf %lf %lf", d, d + 1, d + 2) == 3)
        fwrite(d, sizeof(double), 3, stdout);

    return 0;
}

Compilar:

gcc -o build-fast-trig build-fast-trig.c

Gerando o arquivo de coeficientes

Corre:

octave build-fast-trig.m | grep '^ ' | ./build-fast-trig > qcoeffs.dat

Agora, temos qcoeffs.datcomo arquivo de dados a ser usado no programa atual.

C - parte fast-trig

Salvar como fast-trig.c:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#define INPUT    "qcoeffs.dat"

#define DEGREES    360

typedef struct {double a, b, c;} QCOEFFS;

double normalize(double d)
{
    if (d < 0.0)
        d += ceil(d / -(double)DEGREES) * (double)DEGREES;

    if (d >= (double)DEGREES)
        d -= floor(d / (double)DEGREES) * (double)DEGREES;

    return d;
}

int main()
{
    FILE *f;
    time_t tm;
    double d;
    QCOEFFS qc[DEGREES];

    if (!(f = fopen(INPUT, "rb")) || fread(qc, sizeof(QCOEFFS), DEGREES, f) < DEGREES)
    {
        fprintf(stderr, "Problem with %s - aborting.", INPUT);
        return EXIT_FAILURE;
    }
    fclose(f);

    tm = -clock();

    while (scanf("%lf", &d) > 0)
    {
        int i;
        double e, f;

        /* sin */
        d = normalize(d);
        i = (int)d;
        e = (qc[i].a * d + qc[i].b) * d + qc[i].c;

        /* cos */
        d = normalize((double)DEGREES / 4.0 - d);
        i = (int)d;
        f = (qc[i].a * d + qc[i].b) * d + qc[i].c;

        /* tan = sin / cos */

        /* output - format closest to specs, AFAICT */
        if (d != 0.0 && d != 180.0)
            printf("%.6e %.6e %.6e\n", e, f, e / f);
        else
            printf("%.6e %.6e n\n", e, f);
    }

    tm += clock();

    fprintf(stderr, "time: %.3fs\n", (double)tm/(double)CLOCKS_PER_SEC);    

    return EXIT_SUCCESS;
}

Compilar:

gcc -o fast-trig fast-trig.c -lm

Corre:

./fast-trig < trig.in > trig.out

Ele lerá trig.in, salvará trig.oute 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:

$ ./fast-trig 
0
-6.194924e-19 1.000000e+00 -6.194924e-19

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 exemplo fabs(actual - result) < 1e-06, a entrada será aprovada.

O erro absoluto máximo para sine cosfoi ≤ 3e-07. Pois tan, 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 sine cos, tansendo definido da mesma maneira.


fonte
0

Cobra

class Trig
    const mod as float = 0.0174532925199433f #0.0174532925199432957692369076848861271344287188854172f
    var time as System.Diagnostics.Stopwatch = System.Diagnostics.Stopwatch()
    var file as String[] = File.readAllLines('trig.in')
    var sin_out as float[] = float[](1)
    var cos_out as float[] = float[](1)
    var tan_out as float[] = float[](1)
    def main
        .compute(@[1f])
        .time.reset
        input = float[](.file.length)
        for num, line in .file.numbered, input[num] = float.parse(line)
        .compute(input)
        for num in .file.length, .file[num] = (.sin_out[num].toString('0.000000E+0') + ' ' + .cos_out[num].toString('0.000000E+0') + ' ' + .tan_out[num].toString('0.000000E+0'))
        File.writeAllLines('trig.out', .file)
        print .time.elapsed
    def compute(list as float[])
        .sin_out = float[](list.length)
        .cos_out = float[](list.length)
        .tan_out = float[](list.length)
        .time.start
        for index in list.length
            degrees as float = list[index]
            #add `degrees %= 360` for numbers greater than 360
            rad as float = sin as float = degrees * .mod
            two as float = rad * rad
            sin -= (rad *= two) / 6
            sin += (rad *= two) / 120
            sin -= (rad *= two) / 5040
            sin += (rad *= two) / 362880
            sin -= (rad *= two) / 39916800
            sin += (rad *= two) / 6227020800
            sin -= (rad *= two) / 1307674368000
            sin += (rad *= two) / 355687428096000
            sin -= (rad *= two) / 121645100408832000
            sin += (rad *= two) / 51090942171709440000f
            sin -= (rad *= two) / 25852016738884976640000f
            sin += (rad *= two) / 15511210043330985984000000f
            sin -= (rad *= two) / 10888869450418352160768000000f
            sin += (rad *= two) / 8841761993739701954543616000000f
            cos as float = (1 - (sin * sin)).sqrt * ((degrees - 180).abs - 90).sign
            if cos.isNaN, cos = 0
            .tan_out[index] = Math.round((sin / cos) * 10000000) / 10000000
            .sin_out[index] = Math.round(sin * 10000000) / 10000000
            .cos_out[index] = Math.round(cos * 10000000) / 10000000
        .time.stop

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)

Furioso
fonte
2
Então, além de dar a resposta errada e ser muito lento, está tudo bem? :)
@Lembik Agora está corrigido.
Οurous
4
você percebe que basicamente acabou de escrever o mesmo programa em uma cobra diferente?
Efraim
0

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.

<#include <math.h>                                                 //only for table building and testing
int a;
double t[2926],                                                    //a table for sin x from 0 to 360+90=450deg every 1/6.5 degrees
x,                                                                 //input
s,c,                                                               //first guess sin and cos (from table)
sn,cs,                                                             //output sin and cos
pi1170=3.1415926535897932384626433832795/1170,                     // there are 1170 units of 1/6.5 degrees in a half circle 
pi180=3.1415926535897932384626433832795/180;                       // pi/180 only used for testing

main(){
  for (a=0;a<=2925;a++)t[a]=sin(a*pi1170);                         //build table. 

  scanf("%lf",&x);                                                 //input 
  printf("%le %le %le\n",sin(x*pi180),cos(x*pi180),tan(x*pi180));  //native output for testing purposes

  x*=6.5;                                                          //convert from deg to program units. 6.5=110.1 in binary, a fairly round number. 
  a=x+0.5;                                                         //a=round(x) add 0.5 to round, not truncate. Assigning to int, this is probably faster than the round function.
  x=(x-a)*pi1170;                                                  //(x-a)=dx in program units. Divide to get radians. 

  s=t[a];                                                          //get sin(a) from table
  c=t[a+585];                                                      //cos(a)=sin(a+90degrees)=sin(a+585units)
  sn=s+c*x-s*x*x/2;                                                //sin(x+dx)=sin(x)+cos(dx)-sin(dx^2/2)
  cs=c-s*x-c*x*x/2;                                                //cos(x+dx)=cos(x)-sin(dx)-cos(dx^2/2)
  printf("%le %le %le\n",sn,cs,sn/cs);                             //print sin,cos and tan=sin/cos
}
Level River St
fonte