快速触发计算


16

快速三角计算

您的任务是创建一个程序,该程序可以以度为单位计算角度的正弦,余弦和正切。

规则

  • 没有内置的三角函数(如果您的语言有,则甚至不包括割线,割线和切线)。
  • 您可以使用查找表,但它们的总大小不能超过3000个成员(对于所有三个操作的总和)。请使其从文件(例如trig.lookup)中读取表,以免它们混淆代码。
  • 没有网络访问。
  • 您必须按照以下说明正确舍入输出。不要使用地板或天花板。
  • 您可以使用任何方法来计算值,例如连续分数,只要它对7个有效数字是正确的即可。
  • 您的代码必须能够自行计时。从您的时间中排除文件I / O操作-因此只需对执行触发和任何舍入的函数计时。
  • 我必须能够运行您的代码。请发布指向免费提供的编译器/解释器的链接,并提供编译/运行代码所需的说明(例如,传递给GCC的选项)。
  • 有标准漏洞

输入格式

  • 从名为的文件中读取,trig.in除非您的语言不支持文件I / O。
  • 角度在0到360之间(包括0和360)。
  • 输入将包括与十位有效数字的角度(十进制数字),并用新行分隔。例如:

90.00000000
74.54390000
175.5000000

输出格式

  • 对于提供的每个角度,必须在一行上将其正弦,余弦和切线输出为7个有效数字,并用空格隔开。使用“科学符号”,例如1.745329E-5用于tan 0.0011.000000E+0用于sin 90
  • 用表示无穷或NaN n,例如的输出90.00000000应为1.000000 0.000000 n
  • 如果输入是由换行符分隔的三个角度,则您的输出应包括三条线,每条线包含正弦,余弦和切线。
  • 您可能不会输出其他任何内容。
  • trig.out除非您的语言不支持文件I / O,否则输出到名为的文件。

计分

  • 。面临的挑战是编写一个程序,以尽快计算这三个值。最快的时间获胜。
  • 每个人都会收到许多角度的相同测试输入。
  • 时间将记录在我的机器上。
  • 您的得分是在同一输入上进行三轮测试的平均值(您显然无法在两次测试之间保存任何内容)。
  • 不包括编译时间。这个挑战更多地是关于所使用的方法,而不是语言。(如果有人能指出我如何排除诸如Java之类的语言的编译时间,我将不胜感激)
  • 我的机器是Ubuntu 14.04安装。处理器的统计信息位于Pastebin上(通过运行获取cat /proc/cpuinfo)。
  • 测试完毕后,我会在您的答案中编辑您的时间。

输出是否要在同一行?用回车键格式化时,它看起来是如此漂亮……而且,是否有特定日期选出获胜者?
以法莲2014年

@Ephraim用回车键格式化是什么意思?不,没有具体日期。我确实需要测试所有这些解决方案,但是我尚未进行测试输入;(

@professorfish-看到我的答案中的输出。每一个sincos并且tan是在一个新的生产线。我是否需要更改以将答案输出到一行?
以法莲2014年

2
@Ephraim输出格式真的无关紧要(这不是代码高尔夫球),只要它输出每个角度的sin cos和tan且它们是分开的即可

1
我们应该只对触发计算计时,还是在计时中包括io?
gggg

Answers:


6

Fortran 90

我将CORDIC方法与60个arctan值的预先列表化数组一起使用(有关为何需要这样做的详细信息,请参阅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(Ubuntu存储库中可用4.7或4.8)测试1000个随机角度时,使用ifort 12.1约9.5微秒时,我的测试运行使我每次计算大约需要10微秒。使用Fortran例程仅测试10个随机角度将导致不确定的时间,因为计时例程精确到毫秒,并且简单的数学计算表明,运行所有10个数字都需要0.100毫秒。


编辑显然我在计时IO,这(a)使计时长于必要时间,并且(b)与项目符号6相反。我已经更新了代码以反映这一点。我还发现,将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
qwr14年

1
我认为“ -march = native”是对应于ifort“ -xHost”的gfortran标志。另外,我相信Intel将-O3设置为比gfortran更具侵略性的模式,因此您可以尝试使用带有-O3 -fno-protect-parens -fstack-arrays的gfortran来查看是否有帮助。
2014年

另外,由于您确实在循环内进行了读取,因此您也正在对IO部分进行计时。规则明确规定您不应该计时IO。修复此问题后,计算机上的速度大大提高:每个值0.37微秒,而您发布的代码为6.94微秒。另外,发布的代码无法编译,第100行有一个逗号结尾。第23行也有一个错误:trigs(i)应该只是trigs。这将使发布的代码出现段错误。
2014年

此处的改进版本:pastebin.com/freiHTfx
半外部的

更新以下内容:编译器选项:-march和-fno-protect-parens没有执行任何操作,但是-fstack-arrays减少了每个值0.1微秒。“ ifort -O3 -xHost”的速度明显比“ gfortran -O3 -fstack-arrays”慢2倍: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位元,具有Intel Core i7-3632QM-2.2GHz)

测试:

  • 每次运行时间”是累计花费的时间计算sincostan所有的输入角度。
  • 两者使用的测试输入如下:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


关于代码:
该程序的前提是使用14个项来估计sincos使用其泰勒多项式,这是我计算得出的,以使误差估计小于1e-8是必要的。但是我发现这是更快的计算sincos,因此决定,而不是计算cos使用cos=sqrt(1-sin^2)

麦克劳林罪的系列(x) Maclaurin系列cos(x)


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-我对其进行了修改,因此它现在可以同时在两种语言中工作。
以法莲2014年

您的cos计算过高了,我会这么做sin(x+90degrees)
Skizz 2014年

@Skizz-在我的程序中,我将单词sin既用作函数又用作变量。我认为不必sin()第二次传递某些东西会更快,但是我将两者进行比较以查看是否确实如此。您是否觉得copySign()功能比诸如在我的sin()功能中加起来要慢?
以法莲2014年

啊,我看到你在同时犯罪和犯罪。我的评论只有在您做犯罪或cos时才真正有效。
Skizz 2014年

0

八度(或Matlab)和C

生成过程有点复杂,但采用了一种新颖的方法,结果令人鼓舞。

该方法是为每个度数生成近似二次多项式。因此,度数= [0,1),度数= [1,2),...,度数= [359,360)将各自具有不同的多项式。

八度-建筑部分

Octave可以公开使用-Google download octave

这将确定每个度数的最佳拟合二次多项式。

另存为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,输入将通过。

最大绝对误差sincos为≤3E-07。对于tan,因为结果不限于±1,并且您可以将较大的数除以较小的数,所以绝对误差可能会更大。从-1≤tan(x)≤+1开始,最大绝对误差为≤4e-07。对于tan(x)> 1且tan(x)<-1,最大相对误差,例如fabs((actual - result) / actual)通常<1e-06),直到达到(90±5)或(270±5)度的范围,然后错误变得更糟。

在测试中,每个单输入的平均时间为(1.053±0.007)µs,在我的机器上比本机sin和大约快0.070 µs 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现在已修复。
Οurous

4
您知道您基本上只是在不同的蛇中编写了相同的程序吗?
以法莲

0

C

这是我的尝试。它是这样的:

建立一个从0到450度的sin(x)所有值的表。等效地,这是cos(x)从-90到360度的所有值。对于2926个元素,每1 / 6.5度的值都有足够的空间。因此,程序单位为1 / 6.5度,四分之一圈有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要求。它与本机函数输出匹配,但sin x和cos x的值非常小,与使用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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.