빠른 삼각 계산


16

빠른 삼각법 계산

당신의 임무는 각도의 사인, 코사인 및 탄젠트를 계산할 수있는 프로그램을 만드는 것입니다.

규칙

  • 내장 삼각법 함수가 없습니다 (언어에 해당하는 경우, 상사, 코시컨트 및 코탄젠트도 없음).
  • 찾아보기 테이블을 사용할 수 있지만 총 크기는 3000 명을 초과하지 않아야합니다 (세 가지 작업 모두 합산). trig.lookup코드를 혼동하지 않도록 파일 (예 :)에서 테이블을 읽도록하십시오 .
  • 네트워크에 액세스 할 수 없습니다.
  • 아래 설명에 따라 출력을 올바르게 반올림해야합니다. 바닥이나 천장을 사용하지 마십시오.
  • 유효 숫자 7 자리에 해당하는 한 , 임의의 방법을 사용하여 값 (예 : 연속 분수) 을 계산할 수 있습니다 .
  • 코드 자체가 시간을 정할 수 있어야합니다. 파일 I / O 작업을 시간에서 제외하십시오. 따라서 트리거와 반올림을 수행하는 함수의 시간을 정하십시오.
  • 코드를 실행할 수 있어야합니다. 무료로 사용할 수있는 컴파일러 / 통역사에 대한 링크를 게시하고 코드를 컴파일 / 실행하는 데 필요한 지침을 제공하십시오 (예 : GCC에 전달할 옵션).
  • 표준 허점이 적용됩니다.

입력 형식

  • trig.in언어가 파일 I / O를 지원하지 않는 한 호출 된 파일에서 읽습니다 .
  • 각도는 0에서 360 사이입니다.
  • 입력은 10 개의 유효 숫자에 대한 각도로 구성되며, 새 줄로 구분됩니다. 예를 들면 다음과 같습니다.

90.00000000
74.54390000
175.5000000

출력 형식

  • 제공된 각 각도에 대해 사인, 코사인 및 접선을 공백으로 구분 된 7 개의 유효 숫자로 한 줄에 출력해야합니다. "과학 표기법"을 사용하십시오 (예 : 1.745329E-5for tan 0.001또는 1.000000E+0for) sin 90.
  • 무한대 또는 NaN을 표시 n합니다 (예 :에 대한 출력은 90.00000000이어야 함) 1.000000 0.000000 n.
  • 입력 값이 개행으로 분리 된 3 개의 각도 인 경우 출력은 각각 사인, 코사인 및 탄젠트를 포함하는 3 개의 라인으로 구성되어야합니다.
  • 다른 것을 출력 할 수 없습니다.
  • trig.out언어가 파일 I / O를 지원하지 않는 한 호출 된 파일로 출력합니다 .

채점

  • . 문제는 이러한 세 가지 값을 가능한 빨리 계산하는 프로그램을 작성하는 것입니다. 가장 빠른 시간이 이깁니다.
  • 모든 사람이 여러 각도로 동일한 테스트 입력을받습니다.
  • 내 컴퓨터에 시간이 기록됩니다.
  • 당신의 점수는 같은 입력에서 세 번의 런의 평균입니다 (런 사이에 아무것도 저장할 수는 없습니다).
  • 컴파일 시간은 포함되어 있지 않습니다. 이 도전은 언어보다 사용 된 방법에 관한 것입니다. (누군가가 Java와 같은 언어의 컴파일 시간을 제외시키는 방법을 알려 주면 매우 감사 할 것입니다)
  • 내 컴퓨터는 Ubuntu 14.04 설치입니다. 프로세서의 통계는 Pastebin에 있습니다 (를 실행하여 얻음 cat /proc/cpuinfo).
  • 테스트 한 시간을 답변으로 수정하겠습니다.

출력 한 줄에 있어야 합니까 ? Enter 키로 형식화하면 너무 예쁘게 보입니다. 또한 우승자를 선택하는 특정 날짜가 있습니까?
에브라임

@ Ephraim Enter 키로 포맷했다는 것은 무엇을 의미합니까? 아니요, 특정 날짜가 없습니다. 이 모든 솔루션을 실제로 테스트해야하지만 아직 테스트 입력을하지 않았습니다. (

@professorfish-내 답변의 출력을 참조하십시오. 모든이 sin, cos그리고 tan새로운 라인에 있습니다. 답변을 한 줄에 출력하려면 변경해야합니까?
Ephraim

2
@Ephraim 모든 각도에 대해 sin cos와 tan을 출력하고 그것들이 분리되어있는 한 출력 형식은 실제로 중요하지 않습니다 (코드 골프는 아닙니다)

1
Trig 계산에만 시간을 주거나 타이밍에 io를 포함시켜야합니까?
gggg

답변:


6

포트란 90

저는 60 개의 arctan 값으로 사전에 배열 된 CORDIC 방법을 사용합니다 (필요한 이유에 대한 자세한 내용은 Wiki 기사 참조).

이 코드에는 trig.in개행 파일의 모든 값이 Fortran 실행 파일과 같은 폴더에 저장 되는 파일이 필요합니다 . 이것을 컴파일하면

gfortran -O3 -o file file.f90

file파일 이름은 어디에 있든지간에 ( SinCosTan.f90프로그램 이름과 파일 이름을 일치시킬 필요는 없지만 가장 쉬울 것 입니다). 인텔 컴파일러가있는 경우 다음을 사용하는 것이 좋습니다.

ifort -O3 -xHost -o file file.f90

는 AS -xHost(gfortran 존재하지 않는)은 프로세서에 사용 가능한 높은 수준의 최적화를 제공합니다.

내 테스트 실행은 gfortran 4.4 (Ubuntu repos에서 4.7 또는 4.8을 사용할 수 있음)를 사용하고 ifort 12.1을 사용하여 약 9.5 마이크로 초를 사용하여 1000 개의 임의 각도를 테스트 할 때 계산 당 약 10 마이크로 초를 제공했습니다. 타이밍 루틴이 밀리 초에 정확하고 간단한 수학에 따르면 10 개의 숫자를 모두 실행하려면 0.100 밀리 초가 걸리기 때문에 10 개의 랜덤 각도 만 테스트하면 포트란 루틴을 사용하여 결정 불가능한 시간이 발생합니다.


편집하다 분명히 (a) 타이밍을 필요 이상으로 늘리고 (b) 글 머리 기호 # 6과 반대되는 IO 타이밍이었습니다. 이것을 반영하기 위해 코드를 업데이트했습니다. 또한 kind=8내장 서브 루틴과 함께 정수 를 사용 system_clock하면 마이크로 초의 정확도를 얻을 수 있습니다.

이 업데이트 된 코드로, 지금은 0.3에 대한 마이크로의 삼각 함수의 값의 각 세트 계산하고 (결국 유효 숫자가 달라질에 - 실행 - 실행하지만, 지속적으로 0.31 우리 근처에 유혹 것) 이전부터 상당한 감소를 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

1
"-march = native"는 ifort "-xHost"에 해당하는 gfortran 플래그라고 생각합니다. 또한 인텔은 gfortran보다 -O3이 더 공격적인 모드로 설정되어 있다고 생각하므로 "-O3 -fno-protect-parens -fstack-arrays"를 사용하여 gfortran을 사용해보십시오.
semi-exrinsic

또한 루프 내부를 읽기 때문에 IO 부분의 타이밍도 결정합니다. 규칙은 구체적으로 IO 시간을 지정해서는 안된다고 말합니다. 이 문제를 해결하면 내 컴퓨터의 속도가 크게 향상되었습니다. 값당 0.37 마이크로 초, 게시 된 코드의 경우 6.94입니다. 또한 게시 된 코드가 컴파일되지 않고 100 행에 후행 쉼표가 있습니다. 23 행에도 오류가 있습니다. trigs (i)는 단지 trigs이어야합니다. 이것은 게시 된 코드를 segfault로 만듭니다.
semi-exrinsic

여기에서 개선 된 버전 : pastebin.com/freiHTfx
semi-extrinsic

re 업데이트 : 컴파일러 옵션 : -march 및 -fno-protect-parens는 아무 것도 수행하지 않았지만 -fstack-arrays는 값당 다른 0.1 마이크로 초를 줄였습니다. "ifort -O3 -xHost"는 놀랍게도 "gfortran -O3 -fstack-arrays"보다 거의 2 배 느리다 : 0.55 vs. 0.27
semi-exrinsic

2

Python 2.7.x 또는 Java (선택하십시오)

무료 Python 인터프리터는 여기 에서 다운로드 할 수 있습니다 .
무료 Java 인터프리터는 여기 에서 다운로드 할 수 있습니다 .

프로그램은 trig.in프로그램 파일과 같은 디렉토리에 있는 파일을 입력 할 수 있습니다 . 입력은 개행으로 분리됩니다.

나는 원래 파이썬에서 이것을했기 때문에-나는 파이썬을 좋아합니다. 그러나, 나는 또한 이기기를 원하기 때문에 나중에 자바로 다시 썼다 ...

파이썬 버전 : 컴퓨터에서 실행 당 약 21µs를 얻었습니다. IDEone에서 실행할 때 약 32µs 를 얻었습니다 .

Java 버전 : 컴퓨터에서 실행 당 약 0.4µs, IDEone 에서 1.8µs를 얻습니다 .

컴퓨터 사양 :

  • 인텔 코어 i7-3632QM이 설치된 Windows 8.1 업데이트 1 64 비트-2.2GHz)

테스트:

  • 실행 당 시간 "은을 계산하는 데 걸리는 누적 시간 sin, costan입력 각도의 모든.
  • 두 가지 모두에 사용되는 테스트 입력은 다음과 같습니다.

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


The Code에 대하여 :
이 프로그램의 전제는 14 개의 용어로 Taylor 다항식 을 추정 sin하고 cos사용하는 것이 었습니다. 이것은 1e-8 미만의 오차 추정치가 필요한 것으로 계산되었습니다. 그러나 sin보다 계산하는 것이 더 빠르다는 것을 알았 cos으므로 대신 다음 cos을 사용하여 계산하기로 결정했습니다.cos=sqrt(1-sin^2)

Maclaurin 시리즈의 sin (x) cos (x)의 Maclaurin 시리즈


파이썬 버전 :

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


자바 버전 :

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-나는 그것을 수정 했으므로 이제 두 언어로 작동해야합니다.
Ephraim 2016 년

당신 cos 계산이 과잉입니다, 난 그냥sin(x+90degrees)
Skizz

@Skizz-내 프로그램에서 단어 sin를 함수와 변수로 사용합니다. 나는 sin()두 번째로 무언가를 전달하지 않는 것이 더 빠를 것이라고 생각 했지만 실제로 두 가지를 비교하기 위해 두 가지를 비교할 것입니다. copySign()sin()기능 과 같은 것들을 추가 할 때 기능이 느리다는 인상이 있습니까?
Ephraim

아, 당신이 동시에 죄와 죄를 짓고있는 것을 봅니다. 내 의견은 당신이 죄나 죄를 지었을 때만 실제로 유효 할 것입니다.
Skizz

0

옥타브 (또는 Matlab) & C

약간 복잡한 빌드 프로세스이지만 새로운 접근 방식과 결과는 고무적이었습니다.

이 방법은 각 정도에 대한 근사 이차 다항식을 생성하는 것입니다. 따라서 차수 = [0, 1), 차수 = [1, 2), ..., 차수 = [359, 360)은 각각 다른 다항식을 갖습니다.

옥타브-건물 부분

Octave는 Google에서 공개적으로 사용할 수 있습니다 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-건물 부분

이것은 시스템에서 텍스트 형식의 double을 기본 이진 형식으로 변환합니다.

다른 이름으로 저장 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 합니다.

최대 절대 오차 위해 sin하고 cos있었다 ≤ 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 μS에 대해 하였다 (1.053 ± 0.007) μS이었다 sin하고 cos, tan같은 방식으로 정의된다.


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 이제 수정되었습니다.
OUurous

4
기본적으로 다른 뱀에서 동일한 프로그램을 작성했음을 알고 있습니까?
Ephraim 2016 년

0

여기 내 시도가 있습니다. 다음과 같이 작동합니다.

sin (x)의 모든 값을 0에서 450 도로 표를 만듭니다. 마찬가지로 이것은 -90에서 360도 사이의 모든 cos (x) 값입니다. 2926 개의 요소로 1 / 6.5 도마 다 충분한 공간이 있습니다. 따라서 프로그램 단위는 1 / 6.5도이며 1/4 회전에 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 개의 부동 소수점 연산이 있습니다. 다소 개선 될 수 있습니다. 이 프로그램에는 기본 삼각 함수를 사용한 테이블 작성 및 테스트 출력이 포함되어 있지만 알고리즘에는 없습니다. 나중에 I / O 요구 사항을 준수하도록 타이밍과 편집을 추가합니다 (이번 주말에 권장). sin x 및 cos x의 매우 작은 값을 제외하고 기본 기능 출력과 일치합니다. 약간의 조정.

<#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.