Calcul de déclenchement rapide


16

Calculs rapides de trigonométrie

Votre tâche consiste à créer un programme qui peut calculer le sinus, le cosinus et la tangente d'un angle en degrés.

Règles

  • Pas de fonctions de trigonométrie intégrées (même pas sécantes, cosécantes et cotangentes si votre langue les possède).
  • Vous pouvez utiliser des tables de recherche, mais leur taille totale ne doit pas dépasser 3 000 membres (pour les trois opérations réunies). Veuillez lui faire lire les tableaux d'un fichier (par exemple trig.lookup) afin qu'ils ne confondent pas le code.
  • Pas d'accès au réseau.
  • Vous devez arrondir correctement votre sortie comme expliqué ci-dessous. N'utilisez pas de plancher ou de plafond.
  • Vous pouvez utiliser n'importe quelle méthode pour calculer les valeurs, par exemple des fractions continues , tant qu'elle est correcte à 7 chiffres significatifs.
  • Votre code doit pouvoir se chronométrer. Excluez les opérations d'E / S de fichiers de votre temps - il suffit donc de chronométrer la ou les fonctions qui effectuent le trig et tout arrondi.
  • Je dois pouvoir exécuter votre code. Veuillez poster un lien vers un compilateur / interprète disponible gratuitement et donner les instructions nécessaires pour compiler / exécuter le code (par exemple quelles options passer à GCC).
  • Des échappatoires standard s'appliquent.

Format d'entrée

  • Lisez à partir d'un fichier appelé, trig.insauf si votre langue ne prend pas en charge les E / S de fichiers.
  • Les angles sont compris entre 0 et 360 inclus.
  • L'entrée consistera en angles de dix chiffres significatifs en chiffres décimaux, séparés par de nouvelles lignes. Par exemple:

90,00000000
74,54390000
175,5000000

Format de sortie

  • Pour chaque angle fourni, vous devez afficher ses sinus, cosinus et tangentes sur 7 chiffres significatifs, séparés par des espaces, sur une seule ligne. Utilisez la «notation scientifique», par exemple 1.745329E-5pour tan 0.001ou 1.000000E+0pour sin 90.
  • Désignez l'infini ou NaN par n, par exemple, la sortie de 90.00000000devrait être 1.000000 0.000000 n.
  • Si l'entrée est composée de trois angles séparés par des retours à la ligne, votre sortie doit être composée de trois lignes contenant chacune le sinus, le cosinus et la tangente.
  • Vous ne pouvez rien produire d'autre.
  • Sortie vers un fichier appelé trig.outsauf si votre langue ne prend pas en charge les E / S de fichiers.

Notation

  • . Le défi est d'écrire un programme qui calcule ces trois valeurs le plus rapidement possible. Le temps le plus rapide l'emporte.
  • Tout le monde recevra la même entrée de test sous plusieurs angles.
  • Les temps seront enregistrés sur ma machine.
  • Votre score est la moyenne de trois runs sur la même entrée (vous ne pouvez évidemment rien enregistrer entre les runs).
  • Temps de compilation non inclus. Ce défi concerne plus la méthode utilisée que la langue. (Si quelqu'un pouvait me montrer comment j'exclurais le temps de compilation pour des langages tels que Java, je serais très reconnaissant)
  • Ma machine est une installation d'Ubuntu 14.04. Les statistiques du processeur sont sur Pastebin (obtenues en exécutant cat /proc/cpuinfo).
  • Je modifierai votre temps dans votre réponse lorsque je l'aurai testé.

Est -ce que la sortie a être sur une seule ligne? Il a l'air si joli quand il est formaté avec une touche Entrée ... De plus, y a-t-il une date précise à laquelle un gagnant est choisi?
Ephraim

@Ephraim qu'entendez-vous par formaté avec une touche Entrée? non, il n'y a pas de date précise. J'ai vraiment besoin de tester toutes ces solutions, mais je n'ai pas encore fait d'entrée de test; (

@professorfish - voir le résultat dans ma réponse. Chaque sin, coset tanest sur une nouvelle ligne. Dois-je le modifier pour afficher les réponses sur une seule ligne?
Ephraim

2
@Ephraim Le format de sortie n'a pas vraiment d'importance (ce n'est pas du code-golf) tant qu'il génère les sin cos et tan pour chaque angle et qu'ils sont séparés

1
Sommes-nous censés chronométrer uniquement les calculs trigonométriques ou inclure l'io dans le calendrier?
gggg

Réponses:


6

Fortran 90

J'utilise la méthode CORDIC avec un tableau pré-tabulé de 60 valeurs d'arctan (voir l'article Wiki pour plus de détails sur la raison pour laquelle cela est nécessaire).

Ce code nécessite un fichier, trig.inavec toutes les valeurs des sauts de ligne à stocker dans le même dossier que l'exécutable Fortran. Compiler ceci est,

gfortran -O3 -o file file.f90

fileest le nom de fichier que vous lui donnez (ce SinCosTan.f90serait probablement plus simple, bien qu'il ne soit pas nécessaire de faire correspondre le nom du programme et le nom du fichier). Si vous avez le compilateur Intel, je vous recommande d'utiliser

ifort -O3 -xHost -o file file.f90

car le -xHost(qui n'existe pas pour gfortran) fournit des optimisations de niveau supérieur disponibles pour votre processeur.

Mes tests me donnaient environ 10 microsecondes par calcul lors du test de 1000 angles aléatoires en utilisant gfortran 4.4 (4.7 ou 4.8 est disponible dans les dépôts Ubuntu) et environ 9.5 microsecondes en utilisant ifort 12.1. Le test de seulement 10 angles aléatoires se traduira par un temps indéterminable à l'aide des routines Fortran, car la routine de synchronisation est précise à la milliseconde et les calculs simples indiquent qu'il devrait prendre 0,100 millisecondes pour exécuter les 10 nombres.


EDIT Apparemment, je chronométrais IO, ce qui (a) rendait le chronométrage plus long que nécessaire et (b) est contraire au point n ° 6. J'ai mis à jour le code pour refléter cela. J'ai également découvert que l'utilisation d'un kind=8entier avec le sous-programme intrinsèque system_clockdonne une précision en microsecondes.

Avec ce code mis à jour, je calcule maintenant chaque ensemble de valeurs des fonctions trigonométriques en environ 0,3 microsecondes (les chiffres significatifs à la fin varient d'un cycle à l'autre, mais il oscille constamment près de 0,31 us), une réduction significative par rapport à la précédente. itération qui chronomètre 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
Enfin, quelqu'un a utilisé CORDIC: D
qwr

1
Je pense que "-march = native" est le drapeau gfortran correspondant à ifort "-xHost". De plus, je crois qu'Intel a -O3 réglé sur un mode plus agressif que gfortran, vous pouvez donc essayer gfortran avec "-O3 -fno-protect-parens -fstack-arrays" pour voir si cela aide.
semi-extrinsèque

En outre, vous chronométrez également la partie IO, car vous lisez à l'intérieur de la boucle. Les règles précisent que vous ne devez pas chronométrer IO. La correction de cela a donné une accélération sur mon ordinateur: 0,37 microsecondes par valeur, contre 6,94 pour votre code affiché. De plus, le code publié ne se compile pas, il y a une virgule de fin à la ligne 100. Il y a aussi une erreur à la ligne 23: trigs (i) devrait être juste des trigs. Cela rend le code affiché segfault.
semi-extrinsèque

Version améliorée ici: pastebin.com/freiHTfx
semi-extrinsèque

Mise à jour des options du compilateur: -march et -fno-protect-parens n'ont rien fait, mais -fstack-arrays a réduit de 0,1 microseconde par valeur. "ifort -O3 -xHost" est, remarquablement, presque 2x plus lent que "gfortran -O3 -fstack-arrays": 0,55 contre 0,27
semi-extrinsèque le

2

Python 2.7.x ou Java (faites votre choix)

Un interpréteur Python gratuit peut être téléchargé à partir d' ici .
Un interpréteur Java gratuit peut être téléchargé à partir d' ici .

Le programme peut prendre des entrées à la fois à partir d'un fichier nommé trig.insitué dans le même répertoire que le fichier programme. L'entrée est séparée par des retours à la ligne.

J'ai fait cela à l'origine en python parce que - eh bien, j'adore le python. Mais comme je veux aussi essayer de gagner, je l'ai réécrit en java après ...

Version Python: j'ai obtenu environ 21µs par exécution sur mon ordinateur. J'ai obtenu environ 32µs en l'exécutant sur IDEone .

Version Java: j'obtiens environ 0,4 µs par exécution sur mon ordinateur et 1,8 µs sur IDEone .

Spécifications informatiques:

  • Windows 8.1 mise à jour 1 64 bits avec Intel Core i7-3632QM - 2,2 GHz)

Tester:

  • Le temps par cycle » est le temps cumulatif qu'il faut pour calculer la sin, coset tantous les angles d'entrée.
  • L'entrée de test utilisée pour les deux est la suivante:

    90.00000000  
    74,54390000  
    175.5000000  
    3600000.000  
    


À propos du code:
La prémisse de ce programme était d'estimer sinet d' cosutiliser leurs polynômes de Taylor avec 14 termes, ce qui, selon moi, était nécessaire pour avoir une estimation d'erreur inférieure à 1e-8. Cependant, j'ai trouvé qu'il était plus rapide à calculer sinque cos, alors j'ai décidé de calculer à la place cosen utilisantcos=sqrt(1-sin^2)

Maclaurin série de péché (x) Maclaurin série de cos (x)


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


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

Vos cosinus sont mauvais pour 180 <x <360, et le programme échoue complètement sur 270.
Οurous

@Ourous - Je l'ai modifié, il devrait donc fonctionner maintenant dans les deux langues.
Ephraim

Votre coscalcul est exagéré, je ferais justesin(x+90degrees)
Skizz

@Skizz - Dans mon programme, j'utilise le mot sinà la fois comme fonction et comme variable. Je pensais que ce serait plus rapide de ne pas avoir à passer quelque chose à la sin()deuxième fois, mais je vais comparer les deux pour voir si c'est vraiment le cas. Avez-vous l'impression que la copySign()fonction est plus lente que l'addition de choses comme dans ma sin()fonction?
Ephraim

Ah, je vois que tu fais le péché et le cos en même temps. Mon commentaire ne serait vraiment valable que si vous faisiez du péché ou du cos.
Skizz

0

Octave (ou Matlab) & C

Un processus de construction un peu compliqué, mais une nouvelle approche et les résultats étaient encourageants.

L'approche consiste à générer des polynômes quadratiques approximatifs pour chaque degré. Donc degré = [0, 1), degré = [1, 2), ..., degré = [359, 360) auront chacun un polynôme différent.

Octave - partie bâtiment

Octave est accessible au public - Google download octave .

Cela détermine le polynôme quadratique le mieux adapté à chaque degré.

Enregistrer sous 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 - partie bâtiment

Cela convertit les doubles au format texte en format binaire natif sur votre système.

Enregistrer sous 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;
}

Compiler:

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

Génération du fichier de coefficients

Courir:

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

Maintenant nous avons qcoeffs.dat comme fichier de données à utiliser pour le programme réel.

C - partie à déclenchement rapide

Enregistrer sous 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;
}

Compiler:

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

Courir:

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

Il lira trig.in, enregistrera trig.outet imprimera pour consolider le temps écoulé avec une précision en millisecondes.

Selon les méthodes de test utilisées, il peut échouer sur certaines entrées, par exemple:

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

La sortie correcte devrait être 0.000000e+00 1.000000e+00 0.000000e+00. Si les résultats sont validés à l'aide de chaînes, l'entrée échouera, s'ils sont validés à l'aide d'une erreur absolue, par exemple fabs(actual - result) < 1e-06, l'entrée passera.

L'erreur absolue maximale pour sinet cosétait ≤ 3e-07. Car tan, comme le résultat n'est pas limité à ± 1 et que vous pouvez diviser un nombre relativement grand par un nombre relativement petit, l'erreur absolue pourrait être plus grande. De -1 ≤ tan (x) ≤ +1, l'erreur absolue maximale était ≤ 4e-07. Pour tan (x)> 1 et tan (x) <-1, l' erreur relative maximale , par exemplefabs((actual - result) / actual) était généralement <1e-06 jusqu'à ce que vous atteigniez la zone de (90 ± 5) ou (270 ± 5) degrés, puis le l'erreur s'aggrave.

Lors des tests, le temps moyen par entrée unique était de (1,053 ± 0,007) µs, ce qui sur ma machine était environ 0,070 µs plus rapide que le natif sinet cos, tanétant défini de la même manière.


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

Compilez-le avec cobra filename -turbo

Tests: AMD FX6300 à 5,1 GHz

  • Le test 360 * 10000 utilisé par la réponse C s'exécute en 365 ms (vs 190 ms)

  • Le test à 4 entrées utilisé par les réponses Python et Java s'exécute en 0,32µs (vs 30µs, 3µs)

  • Le test de 1000 angles aléatoires utilisé par la réponse Fortran fonctionne à 100 ns par angle (vs 10 µs)


2
Donc à part donner la mauvaise réponse et être trop lent, ça va? :)

@Lembik C'est maintenant corrigé.
Junurous

4
vous rendez-vous compte que vous venez d'écrire le même programme dans un autre serpent?
Ephraim

0

C

Voici ma tentative. Cela fonctionne comme ceci:

Construisez un tableau de toutes les valeurs de sin (x) de 0 à 450 degrés. De manière équivalente, il s'agit de toutes les valeurs de cos (x) de -90 à 360 degrés. Avec 2926 éléments, il y a suffisamment d'espace pour une valeur tous les 1 / 6,5 degrés. L'unité de programme est donc de 1 / 6,5 degrés, et il y a 585 unités en un quart de tour.

Convertissez les degrés d'entrée en unités de programme (multipliez par 6.5==110.1 binary.) Trouvez dans le tableau les valeurs les plus proches pour sin et cos. puis convertissez la partie restante de l'entrée (dx) en radians.

appliquer la formule sin(x+dx) == sin x +(d(sin x)/dx)*dx.noter que (d(sin x)/dx)==cos x,mais seulement si nous utilisons des radians.

malheureusement, ce n'est pas assez précis en soi, donc un autre terme est requis, basé sur la dérivée suivante d2(sin x)/dx2 == -sin x.Cela doit être multiplié par dx*dx/2(je ne sais pas d'où vient le facteur 2, mais cela fonctionne.)

Suivez la procédure analogue pour cos x, puis calculez tan x == sin x / cos x.

Code

Il y a environ 17 opérations en virgule flottante ici. Cela peut être quelque peu amélioré. Le programme contient la création de tables et la sortie de test en utilisant les fonctions trig natives, mais pas l'algorithme. J'ajouterai le timing et éditerai pour me conformer aux exigences d'E / S plus tard (j'espère ce week-end.) Il correspond à la sortie de la fonction native, sauf pour les très petites valeurs de sin x et cos x, qui devraient être améliorables à mieux que la sortie de la fonction native avec quelques ajustements.

<#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
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.