高速トリガー計算


16

高速三角法計算

あなたの仕事は、角度のサイン、コサイン、タンジェントを度で計算できるプログラムを作成することです。

ルール

  • 組み込みの三角関数はありません(言語にこれらの関数がある場合は、割線、余割、余接もありません)。
  • ルックアップテーブルを使用できますが、それらの合計サイズは3000メンバーを超えてはなりません(3つの操作をすべて合わせて)。trig.lookupそれらがコードを混同しないように、ファイル(例えば)からテーブルを読むようにしてください。
  • ネットワークアクセスなし。
  • 以下で説明するように、出力を正しく丸める必要があります。床や天井を使用しないでください。
  • 有効桁数が7桁である限り、連続分数など、任意の方法を使用して値を計算できます。
  • あなたのコードはそれ自体で時間を計ることができなければなりません。ファイルI / O操作を時間から除外します。トリガーと丸めを行う関数の時間を計ってください。
  • 私はあなたのコードを実行できなければなりません。自由に利用できるコンパイラ/インタプリタへのリンクを投稿し、コードをコンパイル/実行するために必要な指示を与えてください(GCCに渡すオプションなど)。
  • 標準の抜け穴が適用されます。

入力フォーマット

  • trig.inご使用の言語がファイルI / Oをサポートしていない限り、呼び出されたファイルから読み取ります。
  • 角度は0から360までです。
  • 入力は、改行で区切られた10桁の10桁の有効数字までの角度で構成されます。例えば:

90.00000000
74.54390000
175.5000000

出力フォーマット

  • 指定された各角度に対して、スペースで区切られた7つの有効数字のサイン、コサイン、タンジェントを1行で出力する必要があります。1.745329E-5for tan 0.0011.000000E+0for などの「科学表記」を使用しsin 90ます。
  • 無限大またはNaNを表します。nたとえば、の出力はである90.00000000必要があります1.000000 0.000000 n
  • 入力が改行で区切られた3つの角度の場合、出力は3つのラインで構成され、各ラインにはサイン、コサイン、タンジェントが含まれます。
  • 他には何も出力できません。
  • trig.outご使用の言語がファイルI / Oをサポートしていない限り、呼び出されたファイルへの出力。

得点

  • 。課題は、これらの3つの値をできるだけ迅速に計算するプログラムを作成することです。最も速い時間が勝ちます。
  • 誰もが多くの角度の同じテスト入力を受け取ります。
  • 時間は私のマシンに記録されます。
  • スコアは、同じ入力での3回の実行の平均です(実行の間に何も保存できないことは明らかです)。
  • コンパイル時間は含まれていません。この課題は、言語よりも使用される方法に関するものです。(誰かがJavaなどの言語のコンパイル時間を除外する方法を教えてくれたら、とても感謝しています)
  • 私のマシンはUbuntu 14.04インストールです。プロセッサの統計はPastebinにあります(実行により取得されますcat /proc/cpuinfo)。
  • 私はそれをテストしたときにあなたの答えにあなたの時間を編集します。

出力 1行にする必要がありますか?エンターキーでフォーマットされているととてもきれいに見えます...また、勝者が選ばれる特定の日付はありますか?
エフライム14年

@Ephraim Enterキーでフォーマットするとはどういう意味ですか?いいえ、特定の日付はありません。これらすべてのソリューションを実際にテストする必要がありますが、まだテスト入力を行っていません;(

@professorfish-私の答えの出力を参照してください。毎週sincosそしてtan新しい行にあります。回答を1行に出力するように変更する必要がありますか?
エフライム14年

2
あらゆる角度のために長い、それは罪のCoSを出力として黄褐色(これはコードのゴルフではありません)、それらは分離されている出力フォーマットは本当に問題ではない@Ephraim

1
トリガー計算のみを計測するのか、それともタイミングにioを含めるのか?
gggg

回答:


6

Fortran 90

60個のarctan値の事前に配列された配列でCORDICメソッドを採用しています(それが必要な理由の詳細については、Wikiの記事を参照してください)。

このコードには、trig.in改行のすべての値がFortran実行可能ファイルと同じフォルダーに格納されているファイルが必要です。これをコンパイルすると、

gfortran -O3 -o file file.f90

どこにfileファイル名を指定しても(おそらくSinCosTan.f90最も簡単ですが、プログラム名とファイル名を一致させる必要はありません)。Intelコンパイラを使用している場合は、使用することをお勧めします

ifort -O3 -xHost -o file file.f90

-xHost(のgfortranのために存在しない)あなたのプロセッサが利用可能な高いレベルの最適化を提供します。

私のテスト実行では、gfortran 4.4を使用して1000のランダムアングルをテストするときに計算ごとに約10マイクロ秒(Ubuntuリポジトリで4.7または4.8が利用可能)、ifort 12.1を使用して約9.5マイクロ秒でした。10のランダムな角度のみをテストすると、Fortranルーチンを使用すると時間が不確定になります。タイミングルーチンはミリ秒単位で正確であり、簡単な計算では10の数値すべてを実行するには0.100ミリ秒かかるはずです。


編集私はどうやらIOのタイミングを調整していたようです。これを反映するようにコードを更新しました。またkind=8、組み込みサブルーチンで整数を使用すると、system_clockマイクロ秒の精度が得られることも発見しました。

この更新されたコードを使用して、約0.3マイクロ秒で三角関数の値の各セットを計算しています(最後の有効数字は実行ごとに異なりますが、常に0.31 us近くでホバリングしています)。 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
最後に、誰かがCORDIC:D
qwr 14年

1
"-march = native"はifort "-xHost"に対応するgfortranフラグだと思います。また、Intelには-O3がgfortranよりも積極的なモードに設定されているため、「-O3 -fno-protect-parens -fstack-arrays」を指定してgfortranを試して、役立つかどうかを確認できます。
半外因性

また、ループ内で読み取るため、IO部分のタイミングも計っています。ルールは、IOの時間を計るべきではないと具体的に述べています。これを修正すると、私のコンピューターではかなりの高速化が得られました。値ごとに0.37マイクロ秒、投稿されたコードは6.94です。また、投稿されたコードはコンパイルされず、行100に末尾のコンマがあります。行23にもエラーがあります。trigs(i)は単なるtrigである必要があります。これにより、投稿されたコードがセグメンテーション違反になります。
半外因性

ここでの改良版:pastebin.com/freiHTfx
半外因

更新re:コンパイラオプション:-marchおよび-fno-protect-parensは何もしませんでしたが、-fstack-arraysは値ごとにさらに0.1マイクロ秒削り落としました。「のifort -03 -xHost」であり、顕著に、ほぼ2倍より遅い「のgfortran -03 -fstack-アレイ」:0.55対0.27
半外因性

2

Python 2.7.xまたはJava(選択してください)

ここから無料のPythonインタープリターをダウンロードできますここ
から無料のJavaインタープリターをダウンロードできます。

プログラムは、次の名前のファイルから両方を入力できます。 trig.in、プログラムファイルと同じディレクトリにあるます。入力は改行で区切られます。

もともとpythonでこれを行ったのは、-pythonが大好きだからです。しかし、私も勝ちたいと思っているので、その後、javaで書き直しました...

Pythonバージョン:コンピューターでの実行ごとに約21µsを取得しました。IDEoneで実行すると約32µsを取得しました

Javaバージョン:コンピューターでの実行ごとに約0.4µs、IDEone で1.8µsを取得

コンピューターの仕様:

  • Windows 8.1 Update 1 64-bit with Intel Core i7-3632QM-2.2GHz)

テスト:

  • ファイル名を指定して実行」あたりの時間は、計算にかかる累積時間であるsincostan入力角度のすべてを。
  • 両方に使用されるテスト入力は次のとおりです。

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


コードについて:
このプログラムの前提は、14項のTaylor多項式を推定sinしてcos使用することでした。これは、1e-8未満の誤差推定を得るために必要な計算でした。しかし、私はそれがより速く計算するのがわかったsinよりもcos、その代わりに計算することを決定しcos使用することによってcos=sqrt(1-sin^2)

sin(x)のマクラウリン級数 cos(x)のMaclaurinシリーズ


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


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

あなたの余弦は、180 <X <360のために間違っていると、プログラムは270に完全に失敗した
Οurous

@Ourous-変更したので、両方の言語で動作するはずです。
エフライム14年

あなたのcos計算sin(x+90degrees)
はやり

@Skizz-私のプログラムでは、この単語sinを関数と変数の両方として使用しています。sin()もう一度何かを渡す必要がない方が速いと思いましたが、実際にそうであるかどうかを確認するために2つを比較します。copySign()私のsin()機能のように物を足し合わせるよりも機能が遅いという印象はありましたか?
エフライム

ああ、私はあなたが罪と罪を同時にしているのを見ています。私のコメントは、あなたが罪や罪を犯している場合にのみ有効です。
スキズ14年

0

オクターブ(またはMatlab)&C

少し複雑なビルドプロセスですが、新しいアプローチの一種であり、結果は心強いものでした。

アプローチは、次数ごとに近似二次多項式を生成することです。次数= [0、1)、次数= [1、2)、...、次数= [359、360)はそれぞれ異なる多項式を持ちます。

オクターブ-建物の一部

Octaveは公開されている-グーグルdownload octave

これにより、次数ごとに最適な2次多項式が決定されます。

名前を付けて保存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-建物の一部

これにより、システムでテキスト形式の倍精度浮動小数点数がネイティブバイナリ形式に変換されます。

名前を付けて保存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;
}

コンパイル:

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

係数ファイルの生成

実行:

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

これqcoeffs.datで、実際のプログラムに使用するデータファイルができました。

C-高速トリガーパーツ

名前を付けて保存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;
}

コンパイル:

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

実行:

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

ミリ秒の精度で経過時間を読み取りtrig.in、保存しtrig.out、コンソールに出力します。

使用したテスト方法によっては、特定の入力で失敗する場合があります。例:

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

正しい出力はである必要があります0.000000e+00 1.000000e+00 0.000000e+00。結果が文字列を使用して検証される場合、絶対エラーを使用して検証される場合、入力は失敗します。たとえばfabs(actual - result) < 1e-06、入力はパスします。

以下のための最大絶対誤差sincos≤3E-07でした。の場合tan、結果は±1に制限されておらず、比較的大きな数値を比較的小さな数値で除算できるため、絶対誤差が大きくなる可能性があります。-1≤tan(x)≤+1から、最大絶対誤差は≤4e-07でした。tan(x)> 1およびtan(x)<-1の場合、最大相対誤差は、たとえばfabs((actual - result) / actual)(90±5)または(270±5)度の範囲に入るまで通常<1e-06でした。エラーが悪化します。

テストでは、単一の入力あたりの平均時間は、自分のマシン上で高速ネイティブよりも0.070マイクロ秒程度であった(1.053±0.007)マイクロ秒であったsinとはcostan同じように定義されます。


0

コブラ

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

コンパイルする cobra filename -turbo

テスト: AMD FX6300 @ 5.1GHz

  • C回答で使用される360 * 10000テストは365ms(vs 190ms)で実行されます

  • PythonおよびJavaの回答で使用される4エントリテストは、0.32µs(vs 30µs、3µs)で実行されます。

  • Fortranの回答で使用される1000のランダム角度テストは、角度ごとに100ns(vs 10µs)で実行されます。


2
間違った答えを与えて遅すぎることは別として、大丈夫ですか?:)

@Lembik修正されました。
Οurous

4
基本的に、同じプログラムを別のヘビで書いただけだということを知っていますか?
エフライム14年

0

C

これが私の試みです。それはこのように動作します:

0〜450度のsin(x)のすべての値のテーブルを作成します。同様に、これはcos(x)のすべての値が-90〜360度です。2926要素では、1 / 6.5度ごとの値に十分なスペースがあります。したがって、プログラム単位は1 / 6.5度であり、4分の1ターンに585単位があります。

入力次数をプログラム単位に変換します(乗算6.5==110.1 binary.)テーブルからsinとcosの最も近い値を見つけます。次に、入力の残りの部分(dx)をラジアンに変換します。

ラジアンを使用する場合にのみ、式にsin(x+dx) == sin x +(d(sin x)/dx)*dx.注意してください(d(sin x)/dx)==cos x,

残念ながらそれだけでは十分に正確ではないので、次の微分に基づいて別の用語が必要です。d2(sin x)/dx2 == -sin x.これを乗算する必要がありますdx*dx/2(2の因数はどこから来るのかはわかりませんが、機能します)。

同様の手順に従ってください cos x、を計算しtan x == sin x / cos xます。

コード

ここには約17の浮動小数点演算があります。それはいくらか改善できます。プログラムには、ネイティブtrig関数を使用したテーブル構築とテスト出力が含まれていますが、アルゴリズムには含まれていません。タイミングと編集を追加して、後でI / O要件に準拠するようにします(できれば今週の週末)いくつかの微調整。

<#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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.