Calcolo rapido del trigger


16

Calcoli rapidi di trigonometria

Il tuo compito è creare un programma in grado di calcolare il seno, il coseno e la tangente di un angolo in gradi.

Regole

  • Nessuna funzione di trigonometria integrata (nemmeno secante, cosecante e cotangente se la tua lingua li possiede).
  • È possibile utilizzare le tabelle di ricerca, ma la loro dimensione totale non deve superare i 3000 membri (per tutte e tre le operazioni messe insieme). Si prega di farlo leggere le tabelle da un file (ad esempio trig.lookup) in modo che non confondano il codice.
  • Nessun accesso alla rete.
  • È necessario arrotondare correttamente l'output come spiegato di seguito. Non usare il pavimento o il soffitto.
  • È possibile utilizzare qualsiasi metodo per calcolare i valori, ad esempio le frazioni continue , purché sia ​​corretto per 7 cifre significative.
  • Il tuo codice deve essere in grado di temporizzare se stesso. Escludi le operazioni di I / O dei file dal tuo tempo, quindi basta che il tempo le funzioni che eseguono il trig e qualsiasi arrotondamento.
  • Devo essere in grado di eseguire il tuo codice. Pubblica un link a un compilatore / interprete disponibile gratuitamente e fornisci le istruzioni necessarie per compilare / eseguire il codice (ad es. Quali opzioni passare a GCC).
  • Si applicano scappatoie standard .

Formato di input

  • Leggi da un file chiamato a trig.inmeno che la tua lingua non supporti l'I / O dei file.
  • Gli angoli sono compresi tra 0 e 360 ​​inclusi.
  • L'input consisterà in angoli con dieci cifre significative in cifre decimali, separate da nuove linee. Per esempio:

90.00000000
74.54390000
175.5000000

Formato di output

  • Per ogni angolo fornito, è necessario emettere il suo seno, coseno e tangente a 7 cifre significative, separate da spazi, su una sola riga. Usa "notazione scientifica", ad esempio 1.745329E-5per tan 0.001o 1.000000E+0per sin 90.
  • Indicare infinito o NaN con n, ad esempio, l'output per 90.00000000dovrebbe essere 1.000000 0.000000 n.
  • Se l'input è composto da tre angoli separati da newline, l'output dovrebbe essere composto da tre righe, ciascuna contenente seno, coseno e tangente.
  • Non puoi produrre nient'altro.
  • Output su un file chiamato a trig.outmeno che la tua lingua non supporti l'I / O dei file.

punteggio

  • . La sfida è scrivere un programma che calcoli questi tre valori il più rapidamente possibile. Il tempo più veloce vince.
  • Tutti riceveranno lo stesso input di test di molti angoli.
  • I tempi saranno registrati sulla mia macchina.
  • Il tuo punteggio è la media di tre corse sullo stesso input (ovviamente non puoi salvare nulla tra una corsa e l'altra).
  • Tempo di compilazione non incluso. Questa sfida riguarda più il metodo utilizzato che la lingua. (Se qualcuno potesse indicarmi come escluderei il tempo di compilazione per lingue come Java, sarei molto grato)
  • La mia macchina è un'installazione di Ubuntu 14.04. Le statistiche del processore sono su Pastebin (ottenute eseguendo cat /proc/cpuinfo).
  • Modificherò il tuo tempo nella tua risposta quando l'ho provato.

L'output deve essere su una sola riga? Sembra così bello quando è formattato con un tasto Invio ... Inoltre, c'è una data specifica in cui viene scelto un vincitore?
Ephraim,

@Ephraim cosa intendi per formattato con un tasto Invio? no, non c'è una data specifica. Ho davvero bisogno di testare tutte queste soluzioni, ma non ho ancora effettuato il test input; (

@professorfish - vedi l'output nella mia risposta. Ogni sin, cosed tanè su una nuova linea. Devo cambiarlo per generare le risposte su una sola riga?
Efraim,

2
@Ephraim Il formato di output non ha davvero importanza (non si tratta di code-golf) purché

1
Dovremmo cronometrare solo i calcoli dei trigoni o includere lo io nei tempi?
gggg

Risposte:


6

Fortran 90

Utilizzo il CORDIC metodo con un array di pre-tabulato di 60 valori arctan (vedere l'articolo Wiki per dettagli sul perché questo è necessario).

Questo codice richiede un file, trig.incon tutti i valori su newline da archiviare nella stessa cartella dell'eseguibile Fortran. Compilando questo è,

gfortran -O3 -o file file.f90

dove si filetrova qualunque nome di file gli dai (probabilmente SinCosTan.f90sarebbe più semplice, anche se non è necessario abbinare il nome del programma e il nome del file). Se si dispone del compilatore Intel, si consiglia di utilizzare

ifort -O3 -xHost -o file file.f90

poiché -xHost(che non esiste per gfortran) fornisce ottimizzazioni di livello superiore disponibili per il tuo processore.

I miei test mi hanno dato circa 10 microsecondi per calcolo quando ho testato 1000 angoli casuali usando gfortran 4.4 (4.7 o 4.8 è disponibile nei repository Ubuntu) e circa 9.5 microsecondi usando ifort 12.1. Testare solo 10 angoli casuali comporterà un tempo indeterminabile usando le routine di Fortran, poiché la routine di temporizzazione è accurata al millisecondo e la matematica semplice dice che dovrebbero essere necessari 0.100 millisecondi per eseguire tutti i 10 numeri.


EDIT Apparentemente stavo sincronizzando IO che (a) ha reso i tempi più lunghi del necessario e (b) è contrario al punto 6. Ho aggiornato il codice per riflettere questo. Ho anche scoperto che l'uso di un kind=8numero intero con la subroutine intrinseca system_clockoffre una precisione dei microsecondi.

Con questo codice aggiornato, sto ora calcolando ogni serie di valori delle funzioni trigonometriche in circa 0,3 microsecondi (le cifre significative alla fine variano da corsa a corsa, ma si aggira costantemente intorno a 0,31 noi), una riduzione significativa rispetto alla precedente iterazione che ha cronometrato 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

2
Alla fine qualcuno ha usato CORDIC: D
qwr il

1
Penso che "-march = native" sia il flag gfortran corrispondente a ifort "-xHost". Inoltre, credo che Intel abbia -O3 impostato su una modalità più aggressiva di gfortran, quindi potresti provare gfortran con "-O3 -fno-protect-parens -fstack-array" per vedere se aiuta.
semi-estrinseco,

Inoltre, stai sincronizzando anche la parte I / O, poiché leggi all'interno del loop. Le regole specificatamente dicono che non dovresti cronometrare IO. La correzione di questo ha dato abbastanza la velocità sul mio computer: 0,37 microsecondi per valore, rispetto a 6,94 per il tuo codice pubblicato. Inoltre, il codice pubblicato non viene compilato, c'è una virgola finale sulla riga 100. C'è anche un errore sulla riga 23: trigs (i) dovrebbe essere solo trigs. Questo rende il codice pubblicato segfault.
semi-estrinseco

Versione migliorata qui: pastebin.com/freiHTfx
semi-estrinseco

Aggiornamento re: opzioni del compilatore: -march e -fno-protect-parens non hanno fatto nulla, ma -fstack-array si è rasato di altri 0,1 microsecondi per valore. "ifort -O3 -xHost" è, notevolmente, quasi 2x più lento di "gfortran -O3 -fstack-array": 0,55 vs 0.27
semi-estrinseci

2

Python 2.7.xo Java (scegli tu)

Un interprete Python gratuito può essere scaricato da qui .
Un interprete Java gratuito può essere scaricato da qui .

Il programma può ricevere input sia da un file chiamato trig.insituato nella stessa directory del file del programma. L'input è separato da newline.

Inizialmente l'ho fatto in Python perché - beh, adoro Python. Ma, dal momento che voglio provare anche a vincere, lo riscrivo in java dopo ...

Versione Python: ho ottenuto circa 21µs per corsa sul mio computer. Ho ottenuto circa 32µs quando lo eseguo su IDEone .

Versione Java: ottengo circa 0,4 µs per corsa sul mio computer e 1,8 µs su IDEone .

Specifiche del computer:

  • Windows 8.1 aggiornamento 1 a 64 bit con Intel Core i7-3632QM - 2,2 GHz)

Test:

  • Tempo per corsa" è il tempo cumulativo che serve per calcolare la sin, cose tantutti gli angoli di ingresso.
  • L'ingresso di prova utilizzato per entrambi è il seguente:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


Informazioni sul codice:
la premessa per questo programma era di stimare sine cosutilizzare i loro polinomi di Taylor con 14 termini, che era quello che ho calcolato era necessario per avere una stima dell'errore inferiore a 1e-8. Tuttavia ho scoperto che era più veloce da calcolare sindi cos, quindi ho deciso di calcolare invece cosusandocos=sqrt(1-sin^2)

Maclaurin series of sin (x) Serie Maclaurin di cos (x)


Versione 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 '-----------------------------------------------------'


Versione 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;
    }   
}

I tuoi coseni sono errati per 180 <x <360 e il programma fallisce completamente su 270.
Junurous

@Ourous - L'ho modificato, quindi dovrebbe funzionare ora in entrambe le lingue.
Efraim,

Il tuo coscalcolo è eccessivo, lo farei solosin(x+90degrees)
Skizz il

@Skizz - Nel mio programma, utilizzo la parola sinsia come funzione sia come variabile. Ho pensato che sarebbe stato più veloce non dover passare qualcosa alla sin()seconda volta, ma confronterò i due per vedere se è davvero così. La tua impressione è stata che la copySign()funzione sia più lenta dell'aggiunta di cose come nella mia sin()funzione?
Efraim,

Ah, vedo che stai facendo il peccato e il cos allo stesso tempo. Il mio commento sarebbe davvero valido solo se facessi peccato o cos.
Skizz,

0

Ottava (o Matlab) e C

Un processo un po 'complicato, ma un po' come un nuovo approccio e i risultati sono stati incoraggianti.

L'approccio è generare polinomi quadratici approssimativi per ciascun grado. Quindi degree = [0, 1), degree = [1, 2), ..., degree = [359, 360) avranno ciascuno un polinomio diverso.

Ottava - parte dell'edificio

Octave è pubblicamente disponibile - Google download octave .

Questo determina il polinomio quadratico più adatto per ogni grado.

Salva come 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 dell'edificio

Questo converte i doppi in formato testo in formato binario nativo sul tuo sistema.

Salva come 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;
}

Compilare:

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

Generazione del file dei coefficienti

Correre:

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

Ora abbiamo qcoeffs.datcome file di dati da utilizzare per il programma attuale.

C - parte di innesco rapido

Salva come 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;
}

Compilare:

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

Correre:

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

Legge trig.in, salva trig.oute stampa per consolare il tempo trascorso con precisione in millisecondi.

A seconda dei metodi di test utilizzati, potrebbe non riuscire su determinati input, ad esempio:

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

L'output corretto dovrebbe essere 0.000000e+00 1.000000e+00 0.000000e+00. Se i risultati vengono convalidati utilizzando le stringhe, l'input fallirà, se vengono convalidati utilizzando un errore assoluto, ad esempio fabs(actual - result) < 1e-06, l'input passerà.

L'errore assoluto massimo per sined cosera ≤ 3e-07. Perché tan, poiché il risultato non è limitato a ± 1 e puoi dividere un numero relativamente grande per un numero relativamente piccolo, l'errore assoluto potrebbe essere maggiore. Da -1 ≤ tan (x) ≤ +1, l'errore assoluto massimo era ≤ 4e-07. Per l'abbronzatura (x)> 1 e l'abbronzatura (x) <-1, l' errore relativo massimo , ad esempio, fabs((actual - result) / actual)era generalmente <1e-06 fino a quando si arriva nell'area di (90 ± 5) o (270 ± 5) gradi, quindi il l'errore peggiora.

Durante i test, il tempo medio per singolo input era (1,053 ± 0,007) µs, che sulla mia macchina era circa 0,070 µs più veloce di quello nativo sine cos, tanessendo definito allo stesso modo.


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

Compilalo con cobra filename -turbo

Test: AMD FX6300 @ 5.1GHz

  • Il test 360 * 10000 utilizzato dalla risposta C viene eseguito in 365 ms (vs 190 ms)

  • Il test a 4 voci utilizzato dalle risposte Python e Java viene eseguito in 0,32 µs (vs 30 µs, 3 µs)

  • Il test dell'angolo casuale 1000 utilizzato dalla risposta Fortran viene eseguito a 100 ns per angolo (contro 10 µs)


2
Quindi a parte dare la risposta sbagliata ed essere troppo lento, va bene? :)

@Lembik Ora è stato risolto.
Οuroso

4
ti rendi conto di aver appena scritto lo stesso programma in un serpente diverso?
Efraim,

0

C

Ecco il mio tentativo. Funziona così:

Costruisci una tabella di tutti i valori di sin (x) da 0 a 450 gradi. Equivalentemente questo è tutti i valori di cos (x) da -90 a 360 gradi. Con 2926 elementi, c'è spazio sufficiente per un valore ogni 1 / 6,5 gradi. L'unità di programma è quindi 1 / 6,5 gradi e ci sono 585 unità in un quarto di giro.

Convertire i gradi di input in unità di programma (moltiplicare per 6.5==110.1 binary.) Trova i valori più vicini per sin e cos dalla tabella. quindi converti la parte rimanente dell'input (dx) in radianti.

applica la formula sin(x+dx) == sin x +(d(sin x)/dx)*dx.nota che, (d(sin x)/dx)==cos x,ma solo se usiamo i radianti.

sfortunatamente questo non è abbastanza preciso da solo, quindi è necessario un altro termine, basato sulla derivata successiva. d2(sin x)/dx2 == -sin x.Questo deve essere moltiplicato per dx*dx/2(non sono sicuro da dove provenga il fattore 2, ma funziona).

Seguire la procedura analoga per cos x, quindi calcolaretan x == sin x / cos x .

Codice

Ci sono circa 17 operazioni in virgola mobile qui. Questo può essere leggermente migliorato. Il programma contiene la creazione di tabelle e l'output di test utilizzando le funzioni trig native, ma l'algoritmo no. Aggiungerò i tempi e le modifiche per soddisfare i requisiti I / O più tardi (si spera questo fine settimana.) Corrisponde all'output della funzione nativa ad eccezione dei valori molto piccoli di sin x e cos x, che dovrebbero essere migliorabili rispetto all'output della funzione nativa con alcune modifiche.

<#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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.