Hızlı Trig Hesabı


16

Hızlı Trigonometri Hesaplamaları

Göreviniz bir açının sinüsü, kosinüsü ve tanjantını derece cinsinden hesaplayabilen bir program oluşturmaktır.

kurallar

  • Yerleşik trigonometri işlevi yoktur (diliniz varsa sekant, kosekant ve kotanjant bile değildir).
  • Arama tablolarını kullanabilirsiniz, ancak toplam boyutları 3000 üyeyi geçmemelidir (bir araya getirilen üç işlemin tümü için). Lütfen trig.lookupkodu karıştırmamak için tabloları bir dosyadan (ör. ) Okumasını sağlayın .
  • Ağ erişimi yok.
  • Çıktınızı aşağıda açıklandığı gibi doğru şekilde yuvarlamanız gerekir. Zemin veya tavan kullanmayın.
  • 7 önemli şekle doğru olduğu sürece , değerleri, örneğin sürekli kesirleri hesaplamak için herhangi bir yöntemi kullanabilirsiniz .
  • Kodunuzun kendisi zamanlayabilmelidir. Dosya G / Ç işlemlerini zamanınızdan hariç tutun - bu nedenle, trig (ler) i ve herhangi bir yuvarlamayı yapan işlevleri zamanlayın.
  • Kodunuzu çalıştırabilmeliyim. Lütfen serbestçe bulunan bir derleyiciye / yorumlayıcıya bir bağlantı gönderin ve kodu derlemek / çalıştırmak için gerekli talimatları verin (örn. GCC'ye geçmek için hangi seçenekler).
  • Standart boşluklar geçerlidir.

Giriş Formatı

  • Diliniz trig.indosya G / Ç'sini desteklemiyorsa adlı bir dosyadan okuyun .
  • Açılar 0 ile 360 ​​arasındadır.
  • Girdi, yeni satırlarla ayrılmış, ondalık basamaklardaki on önemli sayıya kadar açılardan oluşacaktır. Örneğin:

90.00000000
74.54390000
175.5000000

Çıkış formatı

  • Verilen her açı için, sinüs, kosinüs ve tanjantını boşluklarla ayrılmış 7 önemli rakama tek bir satırda çıkarmalısınız. "Bilimsel gösterim" kullanın, örneğin 1.745329E-5için tan 0.001veya 1.000000E+0için sin 90.
  • Tarafından Göstermek sonsuz veya NaN niçin, örneğin, çıkış 90.00000000olmalıdır 1.000000 0.000000 n.
  • Girdi yeni satırlarla ayrılmış üç açı ise, çıktınız her biri sinüs, kosinüs ve tanjant içeren üç satırdan oluşmalıdır.
  • Başka bir şey çıkaramazsınız.
  • Diliniz trig.outdosya G / Ç'sini desteklemiyorsa, çağrılan bir dosyaya çıktı .

puanlama

  • . Zor olan, bu üç değeri mümkün olan en kısa sürede hesaplayan bir program yazmaktır. En hızlı zaman kazanır.
  • Herkes birçok açı ile aynı test girdisini alacaktır.
  • Saatler makineme kaydedilecek.
  • Puanınız aynı girişteki üç çalışmanın ortalamasıdır (açık aralar arasında hiçbir şeyi kaydedemezsiniz).
  • Derleme süresi dahil değildir. Bu zorluk, kullanılan yöntemden çok dil ile ilgilidir. (Birisi beni Java gibi diller için derleme süresini nasıl hariç tutacağımı gösterebilirse, çok minnettar olurum)
  • Makinem bir Ubuntu 14.04 kurulumudur. İşlemcinin istatistikleri Pastebin'de (çalıştırılarak elde edilir cat /proc/cpuinfo).
  • Test ettiğimde, cevabınızdaki zamanınızı düzenleyeceğim.

Çıkış mu var tek bir satırda olmak? Enter tuşu ile oluşturulduğunda çok güzel görünüyor ... Ayrıca, kazananın seçildiği belirli bir tarih var mı?
Ephraim

@Ephraim bir enter tuşuyla biçimlendirilmiş ile ne demek istiyorsun? Hayır, belirli bir tarih yok. Gerçekten tüm bu çözümleri test etmem gerekiyor, ancak henüz test girdisi yapmadım; (

@professorfish - cevabımdaki çıktıya bakın. Her sin, cosve tanyeni bir hatta. Cevapları tek bir satıra çıkarmak için değiştirmem gerekir mi?
Ephraim

2
Her açı için sürece günah cos verir gibi ve tan (bu kod golf değildir) ve onlar ayrıdır çıkış biçimi gerçekten önemli değil @Ephraim

1
Sadece trig hesaplamalarını zamanlamalı mıyız veya zamanlamaya io dahil etmeli miyiz?
gggg

Yanıtlar:


6

Fortran 90

Önceden tablolanmış bir dizi 60 arctan değerleri ile CORDIC yöntemini kullanıyorum (neden gerekli olduğuna ilişkin ayrıntılar için Wiki makalesine bakın).

Bu kod trig.in, yeni satırlardaki tüm değerlerin Fortran yürütülebilir dosyasıyla aynı klasörde depolanmasını gerektiren bir dosya gerektirir . Bunu derlemek,

gfortran -O3 -o file file.f90

Nerede filebu verin dosya adı ne olursa olsun (muhtemelen edilir SinCosTan.f90o programın adını ve dosya adını eşleşecek şekilde gerekli olmasa, en kolay olacaktır). Intel derleyiciniz varsa,

ifort -O3 -xHost -o file file.f90

olarak -xHost(gfortran için yok olan) İşlemcinize mevcut üst düzey optimizasyonlar sağlar.

Test çalıştırmaları bana gfortran 4.4 (Ubuntu depolarında 4.7 veya 4.8 kullanılabilir) ile 1000 rasgele açı test edilirken hesaplama başına yaklaşık 10 mikrosaniye ve ifort 12.1 kullanarak yaklaşık 9.5 mikrosaniye veriyordu. Sadece 10 rastgele açının test edilmesi, Fortran rutinlerini kullanarak belirsiz bir zamanla sonuçlanacaktır, çünkü zamanlama rutini milisaniye için doğrudur ve basit matematik 10 sayının tümünü çalıştırmak için 0.100 milisaniye sürmesi gerektiğini söylüyor.


DÜZENLE Görünüşe göre (a) zamanlamayı gerekenden daha uzun yapan ve (b) 6. madde işaretine aykırı olan IO zamanlaması yapıyordum. Kodu yansıtacak şekilde güncelledim. Ben de kind=8içsel rutin ile bir tamsayı kullanmanın system_clockmikrosaniye doğruluğu verdiğini keşfettim .

Bu güncellenmiş kodla, şimdi trigonometrik fonksiyonların her bir değer kümesini yaklaşık 0.3 mikrosaniyede hesaplıyorum (sondaki önemli basamaklar çalıştırılmaya değişir, ancak sürekli olarak 0.31 civarında durur), bir öncekinden önemli bir azalma zamanlanmış GÇ yineleme.


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
Son olarak, birisi
CORDIC

1
Ben "-march = yerli" ifort "-xHost" karşılık gfortran bayrağı olduğunu düşünüyorum. Ayrıca, Intel'in -O3'ün gfortrandan daha agresif bir moda ayarladığına inanıyorum, bu yüzden yardımcı olup olmadığını görmek için "-O3 -fno-koru-parens -fstack-dizileri" ile gfortran deneyebilirsiniz.
yarı ekstrinsik

Ayrıca, döngü içinde okuduğunuz için IO parçasını da zamanlarsınız. Kurallar özellikle ES'ye zaman ayırmamanız gerektiğini söylüyor. Bunu düzeltmek bilgisayarımda oldukça hız kazandı: yayınlanan kodunuz için 6.94'e karşılık değer başına 0.37 mikrosaniye. Ayrıca, gönderilen kod derlenmez, 100 satırında bir virgül vardır. 23 satırında da bir hata var: trigs (i) sadece trigs olmalıdır. Bu, yayınlanan kodu segfault yapar.
yarı ekstrinsik

Geliştirilmiş sürüm burada: pastebin.com/freiHTfx
semi-extrinsic

Re: derleyici seçeneklerini güncelleyin: -march ve -fno-protection-parens hiçbir şey yapmadı, ancak -fstack-dizileri değer başına 0,1 mikrosaniye daha tıraş etti. "ifort -O3 -xHost", "gfortran -O3 -fstack-dizilerinden" neredeyse 2 kat daha yavaştır: 0,55'e karşı 0,27
yarı dışsal

2

Python 2.7.x veya Java (Seçiminizi yapın)

Buradan ücretsiz bir Python yorumlayıcısı indirilebilir . Buradan
ücretsiz bir Java tercümanı indirilebilir .

Program trig.in, program dosyasıyla aynı dizinde bulunan adlı bir dosyadan girdi alabilir . Girdi yeni satırlarla ayrılır.

Aslında bunu python'da yaptım çünkü - python'u seviyorum. Ama ben de kazanmayı denemek istediğim için, daha sonra java'da yeniden yazdım ...

Python Sürümü: Bilgisayarımdaki çalışma başına yaklaşık 21µs aldım. IDEone üzerinde çalıştırırken yaklaşık 32µs aldım .

Java sürümü: Bilgisayarımda çalıştırma başına yaklaşık 0.4µs ​​ve IDEone'da 1.8µs alıyorum .

Bilgisayar özellikleri:

  • Intel Core i7-3632QM - 2.2GHz ile Windows 8.1 Güncelleme 1 64-bit)

Ölçek:

  • Run başına Time" hesaplamak için onu alır kümülatif zamandır sin, cosve tangiriş açıları hepsi.
  • Her ikisi için kullanılan test girişi aşağıdaki gibidir:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


Kod Hakkında:
Bu programın dayanağı, 14 terim ile Taylor polinomlarını tahmin etmek sinve coskullanmaktı. Ancak hesaplamaktan sindaha hızlı olduğunu gördüm cos, bu yüzden coskullanarak hesaplamaya karar verdimcos=sqrt(1-sin^2)

Maclaurin serisi günah (x) Maclaurin serisi cos (x)


Python Sürümü:

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


Java Sürümü:

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


@Ourous - Değiştirdim, şimdi her iki dilde de çalışması gerekiyor.
Ephraim

You coshesaplama Sadece yapardım, overkillsin(x+90degrees)
SkizZ

@Skizz - Programımda, kelimeyi sinhem fonksiyon hem de değişken olarak kullanıyorum. sin()İkinci kez bir şey geçirmek zorunda kalmamanın daha hızlı olacağını düşündüm , ancak durumun gerçekten böyle olup olmadığını görmek için ikisini karşılaştıracağım. İşlevim copySign(), işlevim gibi şeyleri eklemekten daha yavaş olduğu izleniminiz oldu sin()mu?
Ephraim

Ah, aynı zamanda günah ve cos yaptığınızı görüyorum. Benim yorumum sadece günah veya koz yapıyorsan geçerli olurdu.
Skizz

0

Oktav (veya Matlab) ve C

Biraz karmaşık bir inşa süreci, ancak yeni bir yaklaşım ve sonuçlar cesaret vericiydi.

Yaklaşım, her derece için yaklaşık ikinci dereceden polinomlar üretmektir. Yani derece = [0, 1), derece = [1, 2), ..., derece = [359, 360) her birinin farklı bir polinomu olacaktır.

Octave - yapı parçası

Octave herkese açık - Google download octave.

Bu, her derece için en uygun kuadratik polinomu belirler.

Farklı kaydet 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 - yapı parçası

Bu, metin biçimindeki çiftleri sisteminizdeki yerel ikili biçime dönüştürür.

Farklı kaydet 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;
}

Derleme:

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

Katsayılar dosyası oluşturma

Çalıştırmak:

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

Şimdi qcoeffs.datgerçek program için kullanılacak veri dosyası olarak var .

C - hızlı tetikleme parçası

Farklı kaydet 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;
}

Derleme:

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

Çalıştırmak:

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

Geçen süreyi milisaniye hassasiyetle okumak için okuyacak trig.in, kaydedecek trig.outve yazdıracaktır.

Kullanılan test yöntemlerine bağlı olarak, belirli girdilerde başarısız olabilir, örneğin:

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

Doğru çıktı olmalıdır 0.000000e+00 1.000000e+00 0.000000e+00. Sonuçlar dize kullanılarak doğrulanırsa, giriş başarısız olur, eğer mutlak hata kullanılarak doğrulanırsa, örneğin fabs(actual - result) < 1e-06, giriş geçer.

Maksimum mutlak hata için sinve cosoldu ≤ 3e-07. İçin tansonuç ± 1 sınırlı değildir ve nispeten küçük sayısına göre oldukça fazla sayıda bölebilirsiniz çünkü, mutlak hata daha büyük olabilir. -1-tan (x) ≤ +1 değerinden, maksimum mutlak hata ≤ 4e-07'dir. Tan (x)> 1 ve tan (x) <-1 için, maksimum bağıl hata, örneğin fabs((actual - result) / actual)(90 ± 5) veya (270 ± 5) derece alana gelene kadar genellikle <1e-06 idi, hata daha da kötüleşir.

Test, tek bir giriş ortalama zaman zaman makinede hızlı doğal daha 0.070 us oldu (1.053 ± 0.007) us, olduğu sinve cos, tanaynı şekilde tarif edilir.


0

kobra

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

İle derleyin cobra filename -turbo

Testler: AMD FX6300 @ 5.1GHz

  • C cevabı tarafından kullanılan 360 * 10000 testi 365 ms'de (190 ms'ye karşı) çalışır

  • Python ve Java cevapları tarafından kullanılan 4 girişli test 0.32µs (30µs, 3µs)

  • Fortran cevabı tarafından kullanılan 1000 rastgele açı testi, açı başına 100ns'de (10µs'ye karşı) çalışır


2
Yanlış yanıtı vermenin ve çok yavaş olmanın yanı sıra, sorun değil mi? :)

@Lembik Artık düzeltildi.
Οurous

4
temelde aynı programı farklı bir yılana yazdığınızı fark ettiniz mi?
Ephraim

0

C

İşte benim girişimim. Şöyle çalışır:

0 ila 450 derece arasındaki tüm günah (x) değerlerinin bir tablosunu oluşturun. Eşdeğer olarak bu, cos (x) 'in tüm değerleri -90 ila 360 derece arasındadır. 2926 element ile, her 1 / 6.5 derecede bir değer için yeterli alan vardır. Program birimi bu nedenle 1 / 6.5 derecedir ve çeyrek turda 585 adet vardır.

Giriş derecelerini program birimlerine dönüştürme (çarpma 6.5==110.1 binary.) Tablodan sin ve cos için en yakın değerleri bulun. daha sonra girişin (dx) kalan kısmını radyana dönüştürün.

formülü uygulamak sin(x+dx) == sin x +(d(sin x)/dx)*dx.not (d(sin x)/dx)==cos x,ama biz Radyanı kullanmak durumunda.

ne yazık ki bu kendi başına yeterince doğru değil, bu yüzden bir sonraki türeve dayalı başka bir terim gerekli d2(sin x)/dx2 == -sin x.Bu ile çarpılmalıdır dx*dx/2(2 faktörünün nereden geldiğinden emin değilim, ama çalışıyor.)

İçin benzer prosedürü takip edin cos x, sonra hesaplayın tan x == sin x / cos x.

kod

Burada yaklaşık 17 kayan nokta işlemi var. Bu biraz geliştirilebilir. Program, yerel trig işlevlerini kullanarak tablo oluşturma ve test çıktısı içerir, ancak algoritma içermez. Daha sonra I / O gereksinimlerine uymak için zamanlama ve düzenleme ekleyeceğim (umarım bu hafta sonu.) Sin x ve cos x'in çok küçük değerleri haricinde yerel işlev çıktısıyla eşleşir. bazı ayarlamalar.

<#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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.