Cálculo de disparo rápido

16

Cálculos de trigonometría rápida

Su tarea es crear un programa que pueda calcular el seno, el coseno y la tangente de un ángulo en grados.

Reglas

  • No hay funciones de trigonometría incorporadas (ni siquiera secante, cosecante y cotangente si su idioma las tiene).
  • Puede usar tablas de búsqueda, pero su tamaño total no debe exceder los 3000 miembros (para las tres operaciones juntas). Por favor, haga que lea las tablas de un archivo (por ejemplo trig.lookup) para que no confundan el código.
  • Sin acceso a internet.
  • Debe redondear correctamente su salida como se explica a continuación. No use piso o techo.
  • Puede usar cualquier método para calcular los valores, por ejemplo , fracciones continuas , siempre que sea correcto para 7 cifras significativas.
  • Su código debe poder cronometrarse. Excluya las operaciones de E / S de archivo de su tiempo, así que solo cronometre las funciones que hacen el trigonometraje y cualquier redondeo.
  • Debo poder ejecutar tu código. Publique un enlace a un compilador / intérprete disponible gratuitamente y brinde las instrucciones necesarias para compilar / ejecutar el código (por ejemplo, qué opciones pasar a GCC).
  • Se aplican lagunas estándar .

Formato de entrada

  • Lea desde un archivo llamado a trig.inmenos que su idioma no admita E / S de archivo.
  • Los ángulos están entre 0 y 360 inclusive.
  • La entrada consistirá en ángulos de diez cifras significativas en dígitos decimales, separados por nuevas líneas. Por ejemplo:

90.00000000
74.54390000
175.5000000

Formato de salida

  • Para cada ángulo suministrado, debe generar su seno, coseno y tangente a 7 figuras significativas, separadas por espacios, en una sola línea. Utilice "notación científica", por ejemplo, 1.745329E-5para tan 0.001o 1.000000E+0para sin 90.
  • Denote infinito o NaN por n, por ejemplo, la salida para 90.00000000debería ser 1.000000 0.000000 n.
  • Si la entrada es de tres ángulos separados por nuevas líneas, su salida debe constar de tres líneas, cada una de las cuales contiene el seno, el coseno y la tangente.
  • No puede enviar nada más.
  • Salida a un archivo llamado a trig.outmenos que su idioma no admita E / S de archivo.

Puntuación

  • . El desafío es escribir un programa que calcule estos tres valores lo más rápido posible. El tiempo más rápido gana.
  • Todos recibirán la misma entrada de prueba de muchos ángulos.
  • Los tiempos se registrarán en mi máquina.
  • Su puntaje es el promedio de tres ejecuciones en la misma entrada (obviamente no puede guardar nada entre ejecuciones).
  • Tiempo de compilación no incluido. Este desafío es más sobre el método utilizado que el lenguaje. (Si alguien pudiera señalarme cómo excluiría el tiempo de compilación para lenguajes como Java, estaría muy agradecido)
  • Mi máquina es una instalación de Ubuntu 14.04. Las estadísticas del procesador están en Pastebin (obtenidas mediante la ejecución cat /proc/cpuinfo).
  • Editaré tu tiempo en tu respuesta cuando lo haya probado.
Geobits
fuente
¿La salida tiene que estar en una sola línea? Se ve tan bonito cuando se formatea con una tecla Intro ... Además, ¿hay una fecha específica en la que se elige un ganador?
Efraín
@Ephraim, ¿qué quieres decir con formateado con una tecla enter? No, no hay una fecha específica. Realmente necesito probar todas estas soluciones, pero aún no he hecho la entrada de prueba; (
@professorfish: vea el resultado en mi respuesta. Todos sin, cosy tanestá en una nueva línea. ¿Necesito cambiarlo para generar las respuestas en una sola línea?
Efraín
2
@Ephraim El formato de salida realmente no importa (esto no es code-golf) siempre y cuando
1
¿Se supone que debemos cronometrar solo los cálculos trigonométricos, o incluir el io en el tiempo?
gggg

Respuestas:

6

Fortran 90

Empleo el CORDIC método con una matriz de pre-tabulados de 60 valores arctan (véase el artículo Wiki para más detalles sobre por qué es necesario).

Este código requiere un archivo, trig.incon todos los valores en las nuevas líneas que se almacenarán en la misma carpeta que el ejecutable de Fortran. Compilar esto es,

gfortran -O3 -o file file.f90

donde fileestá el nombre de archivo que le dé (probablemente SinCosTan.f90sería más fácil, aunque no es necesario que coincida con el nombre del programa y el nombre del archivo). Si tiene el compilador Intel, le recomiendo usar

ifort -O3 -xHost -o file file.f90

ya -xHostque (que no existe para gfortran) proporciona optimizaciones de mayor nivel disponibles para su procesador.

Mis pruebas me dieron unos 10 microsegundos por cálculo al probar 1000 ángulos aleatorios usando gfortran 4.4 (4.7 o 4.8 está disponible en los repositorios de Ubuntu) y alrededor de 9.5 microsegundos usando ifort 12.1. Probar solo 10 ángulos aleatorios dará como resultado un tiempo indeterminable usando las rutinas de Fortran, ya que la rutina de tiempo es precisa al milisegundo y las matemáticas simples dicen que debería tomar 0.100 milisegundos para ejecutar los 10 números.


EDITAR Aparentemente estaba cronometrando IO que (a) hizo que el cronometraje fuera más largo de lo necesario y (b) es contrario a la viñeta # 6. He actualizado el código para reflejar esto. También descubrí que usar un kind=8número entero con la subrutina intrínseca system_clockda una precisión de microsegundos.

Con este código actualizado, ahora estoy calculando cada conjunto de valores de las funciones trigonométricas en aproximadamente 0.3 microsegundos (los dígitos significativos al final varían de ejecución a ejecución, pero constantemente se cierne cerca de 0.31 us), una reducción significativa del anterior iteración que cronometró 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
fuente
2
Finalmente, alguien usó CORDIC: D
qwr
1
Creo que "-march = native" es la bandera de gfortran correspondiente a ifort "-xHost". Además, creo que Intel tiene -O3 configurado en un modo más agresivo que gfortran, por lo que podría probar gfortran con "-O3 -fno-protect-parens -fstack-arrays" para ver si ayuda.
semi-extrínseco
Además, también está cronometrando la parte IO, ya que lee dentro del bucle. Las reglas específicamente dicen que no debes cronometrar IO. Arreglar esto dio bastante velocidad en mi computadora: 0.37 microsegundos por valor, frente a 6.94 para su código publicado. Además, el código publicado no se compila, hay una coma final en la línea 100. También hay un error en la línea 23: trigs (i) debería ser solo trigs. Esto hace que el código publicado sea predeterminado.
semi-extrínseco
Versión mejorada aquí: pastebin.com/freiHTfx
semi-extrínseco
Actualización re: opciones del compilador: -march y -fno-protect-parens no hicieron nada, pero -fstack-arrays redujeron otros 0.1 microsegundos por valor. "ifort -O3 -xHost" es, notablemente, casi 2 veces más lento que "gfortran -O3 -fstack-arrays": 0.55 vs. 0.27
semi-extrínseco
2

Python 2.7.xo Java (elija)

Se puede descargar un intérprete gratuito de Python desde aquí .
Se puede descargar un intérprete gratuito de Java desde aquí .

El programa puede recibir información de un archivo llamado trig.inubicado en el mismo directorio que el archivo del programa. La entrada está separada por nuevas líneas.

Originalmente hice esto en Python porque, bueno, me encanta Python. Pero, dado que quiero tratar de ganar también, lo reescribí en Java después ...

Versión de Python: obtuve alrededor de 21 µs por ejecución en mi computadora. Obtuve unos 32 µs cuando lo ejecuté en IDEone .

Versión de Java: obtengo alrededor de 0.4 µs por ejecución en mi computadora y 1.8 µs en IDEone .

Especificaciones de la computadora:

  • Windows 8.1 actualización 1 de 64 bits con Intel Core i7-3632QM - 2.2GHz)

Prueba:

  • Tiempo por ejecución "es el tiempo acumulado que lleva calcular el sin,cos y tantodos los ángulos de entrada.
  • La entrada de prueba utilizada para ambos es la siguiente:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


Acerca del Código:
La premisa de este programa era estimar siny cosusar sus polinomios de Taylor con 14 términos, que fue lo que calculé que era necesario para tener una estimación de error de menos de 1e-8. Sin embargo, descubrí que era más rápido de calcular sinque cos, así que decidí calcular cosusandocos=sqrt(1-sin^2)

Maclaurin series of sin (x) Maclaurin serie de cos (x)


Versión de 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 '-----------------------------------------------------'


Versión de 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;
    }   
}
Efraín
fuente
Sus cosenos son incorrectas para 180 <x <360, y el programa falla por completo en 270.
Οurous
@Ourous: lo modifiqué, por lo que debería funcionar ahora en ambos idiomas.
Efraín
Tu coscálculo es excesivo, solo lo haríasin(x+90degrees)
Skizz
@Skizz: en mi programa, uso la palabra sincomo una función y como una variable. Pensé que sería más rápido no tener que pasarle algo por sin()segunda vez, pero compararé los dos para ver si ese es realmente el caso. ¿Le pareció que la copySign()función es más lenta que sumar cosas como en mi sin()función?
Efraín
Ah, veo que estás haciendo pecado y cos al mismo tiempo. Mi comentario solo sería realmente válido si estuvieras haciendo pecado o cos.
Skizz
0

Octava (o Matlab) y C

Un proceso de construcción un poco complicado, pero un enfoque novedoso y los resultados fueron alentadores.

El enfoque consiste en generar polinomios cuadráticos aproximados para cada grado. Entonces grado = [0, 1), grado = [1, 2), ..., grado = [359, 360) tendrán cada uno un polinomio diferente.

Octave - parte de construcción

Octave está disponible públicamente: Google download octave.

Esto determina el mejor polinomio cuadrático para cada grado.

Guardar 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 de construcción

Esto convierte los dobles en formato de texto a formato binario nativo en su sistema.

Guardar 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

Generando el archivo de coeficientes

Correr:

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

Ahora tenemos qcoeffs.datcomo archivo de datos para usar para el programa real.

C - parte de activación rápida

Guardar 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

Correr:

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

Leerá trig.in, guardará trig.oute imprimirá para consolar el tiempo transcurrido con una precisión de milisegundos.

Dependiendo de los métodos de prueba utilizados, puede fallar en ciertas entradas, por ejemplo:

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

La salida correcta debería ser 0.000000e+00 1.000000e+00 0.000000e+00. Si los resultados se validan usando cadenas, la entrada fallará, si se validan usando un error absoluto, por ejemplo fabs(actual - result) < 1e-06, la entrada pasará.

El error absoluto máximo para siny cosfue ≤ 3e-07. Porque tan, dado que el resultado no se limita a ± 1 y puede dividir un número relativamente grande por un número relativamente pequeño, el error absoluto podría ser mayor. De -1 ≤ tan (x) ≤ +1, el error absoluto máximo fue ≤ 4e-07. Para tan (x)> 1 y tan (x) <-1, el error relativo máximo , por ejemplo, fabs((actual - result) / actual)fue generalmente <1e-06 hasta llegar al área de (90 ± 5) o (270 ± 5) grados, luego el El error empeora.

En las pruebas, el tiempo promedio por entrada individual fue (1.053 ± 0.007) µs, que en mi máquina fue aproximadamente 0.070 µs más rápido que el nativo siny cos, tanal definirse de la misma manera.


fuente
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

Compilarlo con cobra filename -turbo

Pruebas: AMD FX6300 @ 5.1GHz

  • La prueba 360 * 10000 utilizada por la respuesta C se ejecuta en 365 ms (frente a 190 ms)

  • La prueba de 4 entradas utilizada por las respuestas de Python y Java se ejecuta en 0,32 µs (frente a 30 µs, 3 µs)

  • La prueba de 1000 ángulos aleatorios utilizada por la respuesta de Fortran se ejecuta a 100 ns por ángulo (frente a 10 µs)

Οurous
fuente
2
Entonces, aparte de dar la respuesta incorrecta y ser demasiado lento, ¿está bien? :)
@Lembik Ahora está arreglado.
Οurous
44
¿Te das cuenta de que básicamente escribiste el mismo programa en una serpiente diferente?
Efraín
0

C

Aquí está mi intento. Funciona así:

Construya una tabla de todos los valores de sin (x) de 0 a 450 grados. De manera equivalente, todos estos valores son cos (x) de -90 a 360 grados. Con 2926 elementos, hay suficiente espacio para un valor cada 1 / 6.5 grados. La unidad del programa es, por lo tanto, 1 / 6.5 grados, y hay 585 unidades en un cuarto de vuelta.

Convierta los grados de entrada en unidades del programa (multiplique por 6.5==110.1 binary.) Encuentre los valores más cercanos para sen y cos de la tabla. luego convierta la parte restante de la entrada (dx) en radianes.

aplicamos la fórmula sin(x+dx) == sin x +(d(sin x)/dx)*dx.nota que (d(sin x)/dx)==cos x,pero solo si usamos radianes.

desafortunadamente, eso no es lo suficientemente preciso por sí solo, por lo que se requiere otro término, basado en la siguiente derivada. d2(sin x)/dx2 == -sin x.Esto debe multiplicarse por dx*dx/2(no estoy seguro de dónde proviene el factor 2, pero funciona).

Siga el procedimiento análogo para cos x, luego calcule tan x == sin x / cos x.

Código

Hay alrededor de 17 operaciones de punto flotante aquí. Eso se puede mejorar un poco. El programa contiene la creación de tablas y resultados de prueba utilizando las funciones trigonométricas nativas, pero el algoritmo no. Agregaré el tiempo y la edición para cumplir con los requisitos de E / S más adelante (con suerte este fin de semana). Coincide con la salida de la función nativa, excepto por valores muy pequeños de sen x y cos x, que deberían mejorarse mejor que la salida de la función nativa con Algunos 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
fuente