Tính toán Trig nhanh


16

Tính toán lượng giác nhanh

Nhiệm vụ của bạn là tạo ra một chương trình có thể tính toán sin, cosin và tiếp tuyến của một góc theo độ.

Quy tắc

  • Không có các hàm lượng giác tích hợp (thậm chí không bí mật, cosecant và cotangent nếu ngôn ngữ của bạn có chúng).
  • Bạn có thể sử dụng các bảng tra cứu, nhưng tổng kích thước của chúng không được vượt quá 3000 thành viên (đối với cả ba thao tác được đặt cùng nhau). Vui lòng làm cho nó đọc các bảng từ một tệp (ví dụ trig.lookup) để chúng không nhầm lẫn mã.
  • Không có quyền truy cập mạng.
  • Bạn phải làm tròn chính xác đầu ra của bạn như được giải thích dưới đây. Không sử dụng sàn hoặc trần.
  • Bạn có thể sử dụng bất kỳ phương pháp nào để tính toán các giá trị, ví dụ như các phân số tiếp tục , miễn là nó đúng với 7 số liệu có ý nghĩa.
  • Mã của bạn phải có thời gian chính nó. Loại trừ các hoạt động I / O của tập tin khỏi thời gian của bạn - vì vậy chỉ cần thời gian (các) chức năng thực hiện trig và bất kỳ làm tròn nào.
  • Tôi phải có khả năng chạy mã của bạn. Vui lòng gửi một liên kết đến một trình biên dịch / trình thông dịch có sẵn miễn phí và đưa ra các hướng dẫn cần thiết để biên dịch / chạy mã (ví dụ: các tùy chọn nào để chuyển đến GCC).
  • Tiêu chuẩn áp dụng.

Định dạng đầu vào

  • Đọc từ một tệp được gọi trig.intrừ khi ngôn ngữ của bạn không hỗ trợ I / O tệp.
  • Các góc có từ 0 đến 360.
  • Đầu vào sẽ bao gồm các góc tới mười số có nghĩa trong các chữ số thập phân, được phân tách bằng các dòng mới. Ví dụ:

90.00000000
74.54390000
175.5000000

Định dạng đầu ra

  • Đối với mỗi góc được cung cấp, bạn phải xuất hình sin, cosin và tiếp tuyến của nó thành 7 hình có ý nghĩa, được phân tách bằng khoảng trắng, trên một dòng. Sử dụng "ký hiệu khoa học", ví dụ 1.745329E-5cho tan 0.001hoặc 1.000000E+0cho sin 90.
  • Vô biểu thị hoặc NaN bởi n, ví dụ như đầu ra cho 90.00000000nên 1.000000 0.000000 n.
  • Nếu đầu vào là ba góc cách nhau bởi dòng mới, đầu ra của bạn sẽ bao gồm ba dòng, mỗi dòng chứa sin, cos và tiếp tuyến.
  • Bạn không thể xuất bất cứ thứ gì khác.
  • Xuất ra một tệp được gọi trig.outtrừ khi ngôn ngữ của bạn không hỗ trợ I / O tệp.

Chấm điểm

  • . Thách thức là viết một chương trình tính toán ba giá trị này càng nhanh càng tốt. Thời gian nhanh nhất chiến thắng.
  • Mọi người sẽ nhận được cùng một đầu vào thử nghiệm của nhiều góc độ.
  • Thời gian sẽ được ghi lại trên máy của tôi.
  • Điểm của bạn là trung bình của ba lần chạy trên cùng một đầu vào (bạn không thể lưu bất cứ điều gì ở giữa các lần chạy rõ ràng).
  • Thời gian biên dịch không bao gồm. Thách thức này là về phương pháp được sử dụng hơn ngôn ngữ. (Nếu ai đó có thể chỉ cho tôi cách tôi loại trừ thời gian biên dịch cho các ngôn ngữ như Java, tôi sẽ rất biết ơn)
  • Máy của tôi là bản cài đặt Ubuntu 14.04. Số liệu thống kê của bộ xử lý là trên Pastebin (thu được bằng cách chạy cat /proc/cpuinfo).
  • Tôi sẽ chỉnh sửa thời gian của bạn thành câu trả lời của bạn khi tôi đã kiểm tra nó.

Có đầu ra phải nằm trên một dòng không? Nó trông rất đẹp khi được tạo thành bằng một phím enter ... Ngoài ra, có một ngày cụ thể mà người chiến thắng được chọn không?
Ephraim

@Ephraim bạn có ý gì khi định dạng bằng phím enter? không, không có một ngày cụ thể Tôi thực sự cần phải thử nghiệm tất cả các giải pháp này, nhưng tôi chưa thực hiện đầu vào thử nghiệm; (

@professorfish - xem đầu ra trong câu trả lời của tôi. Mỗi sin, costanlà trên một dòng mới. Tôi có cần thay đổi nó để đưa ra câu trả lời trên một dòng không?
Ephraim

2
@Ephraim Định dạng đầu ra thực sự không thành vấn đề (đây không phải là môn đánh gôn) miễn là nó tạo ra cos cos và tan cho mọi góc và chúng riêng biệt

1
Chúng ta có nên chỉ tính thời gian trig, hoặc bao gồm io trong thời gian?
gggg

Câu trả lời:


6

Pháo đài 90

Tôi sử dụng phương pháp CORDIC với một mảng 60 giá trị arctan được lập bảng trước (xem bài viết Wiki để biết chi tiết về lý do cần thiết).

Mã này yêu cầu một tệp, trig.invới tất cả các giá trị trên dòng mới sẽ được lưu trong cùng thư mục với tệp thực thi Fortran. Biên dịch này là,

gfortran -O3 -o file file.f90

filebạn đặt tên tệp ở đâu (có lẽ SinCosTan.f90là dễ nhất, mặc dù không cần phải khớp tên chương trình và tên tệp). Nếu bạn có trình biên dịch Intel, tôi khuyên bạn nên sử dụng

ifort -O3 -xHost -o file file.f90

-xHost(không tồn tại cho gfortran) cung cấp tối ưu hóa mức cao hơn có sẵn cho bộ xử lý của bạn.

Các lần chạy thử của tôi đã cho tôi khoảng 10 micro giây mỗi lần tính toán khi kiểm tra 1000 góc ngẫu nhiên bằng gfortran 4.4 (4.7 hoặc 4.8 có sẵn trong repos Ubuntu) và khoảng 9,5 micro giây khi sử dụng ifort 12.1. Chỉ kiểm tra 10 góc ngẫu nhiên sẽ dẫn đến thời gian không xác định bằng cách sử dụng các thói quen của Fortran, vì thói quen thời gian là chính xác đến mili giây và toán học đơn giản nói rằng phải mất 0,100 mili giây để chạy tất cả 10 số.


EDIT Rõ ràng tôi đã định thời gian IO mà (a) làm cho thời gian dài hơn mức cần thiết và (b) trái với viên đạn số 6. Tôi đã cập nhật mã để phản ánh điều này. Tôi cũng đã phát hiện ra rằng việc sử dụng một kind=8số nguyên với chương trình con nội tại system_clockmang lại độ chính xác đến từng giây.

Với mã được cập nhật này, tôi hiện đang tính toán từng bộ giá trị của các hàm lượng giác trong khoảng 0,3 micro giây (các chữ số có nghĩa cuối cùng thay đổi khi chạy, nhưng nó luôn lơ lửng gần 0,31 chúng tôi), giảm đáng kể so với trước lặp đi lặp lại mà hẹn giờ 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
Cuối cùng, ai đó đã sử dụng CORDIC: D
qwr

1
Tôi nghĩ rằng "-march = bản địa" là cờ gfortran tương ứng với ifort "-xhost". Ngoài ra, tôi tin rằng Intel có -O3 được đặt ở chế độ mạnh hơn gfortran, vì vậy bạn có thể thử gfortran với "-O3 -fno-bảo vệ-parens -fstack-mảng" để xem có giúp ích gì không.
bán bên ngoài

Ngoài ra, bạn cũng đang định thời gian cho phần IO, vì bạn đã đọc bên trong vòng lặp. Các quy tắc cụ thể nói rằng bạn không nên thời gian IO. Việc sửa lỗi này đã giúp tăng tốc khá nhanh trên máy tính của tôi: 0,37 micro giây trên mỗi giá trị, so với 6,94 cho mã được đăng của bạn. Ngoài ra, mã được đăng không biên dịch, có dấu phẩy ở dòng 100. Ngoài ra còn có lỗi trên dòng 23: trigs (i) chỉ là trigs. Điều này làm cho mã segfault được đăng.
bán ngoài

Phiên bản cải tiến tại đây: pastebin.com/freiHTfx
semi-extrinsic

Cập nhật lại: tùy chọn trình biên dịch: -march và -fno-Protect-parens không làm gì cả, nhưng -fstack-mảng đã loại bỏ 0,1 micro giây khác cho mỗi giá trị. "Ifort -O3 -xhost", đáng chú ý, chậm hơn gần gấp 2 lần so với "gfortran -O3 -fstack-mảng": 0,55 so với 0,27
bán ngoài

2

Python 2.7.x hoặc Java (Hãy lựa chọn)

Một trình thông dịch Python miễn phí có thể được tải xuống từ đây .
Một trình thông dịch Java miễn phí có thể được tải xuống từ đây .

Chương trình có thể lấy cả hai từ một tệp có tên trig.introng cùng thư mục với tệp chương trình. Đầu vào được phân tách bằng dòng mới.

Ban đầu tôi đã làm điều này trong python bởi vì - tốt, tôi yêu python. Nhưng, vì tôi cũng muốn cố gắng để giành chiến thắng, tôi đã viết lại nó trong java sau đó ...

Phiên bản Python: Tôi đã nhận được khoảng 21 Lời nói trên mỗi lần chạy trên máy tính của mình. Tôi đã nhận được khoảng 32 chuông khi chạy nó trên IDEone .

Phiên bản Java: Tôi nhận được khoảng 0,4 điều mỗi lần chạy trên máy tính của tôi và 1,8 Lời nói trên IDEone .

Thông số kỹ thuật máy tính:

  • Windows 8.1 cập nhật 1 64-bit với lõi intel i7-3632QM - 2.2GHz)

Kiểm tra:

  • Thời gian mỗi lần chạy" là thời gian tích lũy cần thiết để tính toán sin, costantất cả các góc độ đầu vào.
  • Đầu vào thử nghiệm được sử dụng cho cả hai như sau:

    90.00000000  
    74,54390000  
    175.5000000  
    3600000.000  
    


Về Quy tắc:
Tiền đề cho chương trình này là ước tính sincossử dụng đa thức Taylor của họ với 14 điều khoản, đó là điều tôi tính toán là cần thiết để có ước tính sai số nhỏ hơn 1e-8. Tuy nhiên tôi thấy nó nhanh hơn để tính toán sinhơn cos, vì vậy quyết định thay vì tính toán cosbằng cách sử dụngcos=sqrt(1-sin^2)

Chuỗi Maclaurin của tội lỗi (x) Dòng Maclaurin của cos (x)


Phiên bản 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 '-----------------------------------------------------'


Phiên bản 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;
    }   
}


@Ourous - Tôi đã sửa đổi nó, vì vậy nó sẽ hoạt động với cả hai ngôn ngữ.
Ephraim

Bạn costính toán quá mức, tôi chỉ cần làmsin(x+90degrees)
Skizz

@Skizz - Trong chương trình của tôi, tôi sử dụng từ này sinvừa là hàm, vừa là biến. Tôi nghĩ sẽ nhanh hơn khi không phải chuyển thứ gì đó đến sin()lần thứ hai, nhưng tôi sẽ so sánh hai thứ đó để xem đó có phải là trường hợp thực sự không. Có phải ấn tượng của bạn là copySign()chức năng chậm hơn khi thêm những thứ như trong sin()chức năng của tôi ?
Ephraim

À, tôi thấy bạn đang làm tội lỗi và cos cùng một lúc. Nhận xét của tôi sẽ chỉ thực sự có giá trị nếu bạn đang làm tội lỗi hoặc cos.
Skizz

0

Octave (hoặc Matlab) & C

Một chút của một quá trình xây dựng phức tạp, nhưng một cách tiếp cận mới lạ và kết quả rất đáng khích lệ.

Cách tiếp cận là tạo ra các đa thức bậc hai xấp xỉ cho mỗi độ. Vì vậy độ = [0, 1), độ = [1, 2), ..., độ = [359, 360) mỗi loại sẽ có một đa thức khác nhau.

Octave - xây dựng một phần

Octave có sẵn công khai - Google download octave.

Điều này xác định đa thức bậc hai phù hợp nhất cho mọi mức độ.

Lưu dưới dạng 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 - phần xây dựng

Điều này chuyển đổi gấp đôi trong định dạng văn bản thành định dạng nhị phân gốc trên hệ thống của bạn.

Lưu dưới dạng 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;
}

Biên dịch:

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

Tạo tập tin hệ số

Chạy:

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

Bây giờ chúng ta có qcoeffs.dattệp dữ liệu để sử dụng cho chương trình thực tế.

C - phần trig nhanh

Lưu dưới dạng 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;
}

Biên dịch:

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

Chạy:

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

Nó sẽ đọc từ trig.in, lưu vào trig.outvà in để điều khiển thời gian trôi qua với độ chính xác đến mili giây.

Tùy thuộc vào các phương pháp thử nghiệm được sử dụng, nó có thể thất bại ở một số đầu vào nhất định, ví dụ:

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

Đầu ra chính xác phải là 0.000000e+00 1.000000e+00 0.000000e+00. Nếu kết quả được xác thực bằng chuỗi, đầu vào sẽ thất bại, nếu chúng được xác thực bằng lỗi tuyệt đối, ví dụ: fabs(actual - result) < 1e-06đầu vào sẽ vượt qua.

Lỗi tuyệt đối tối đa cho sincoslà ≤ 3e-07. Vì tan, vì kết quả không giới hạn ở ± 1 và bạn có thể chia một số tương đối lớn cho một số tương đối nhỏ, nên sai số tuyệt đối có thể lớn hơn. Từ -1 ≤ tan (x) +1, sai số tuyệt đối tối đa là ≤ 4e-07. Đối với tan (x)> 1 và tan (x) <-1, sai số tương đối tối đa , ví dụ: fabs((actual - result) / actual)thường là <1e-06 cho đến khi bạn ở trong khu vực (90 ± 5) hoặc (270 ± 5) độ, sau đó lỗi trở nên tồi tệ hơn

Trong thử nghiệm, thời gian trung bình cho mỗi đầu vào là (1.053 ± 0,007), mà trên máy của tôi nhanh hơn khoảng 0,070 mật so với bản gốc sincos, tanđược định nghĩa theo cùng một cách.


0

Rắn hổ mang

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

Biên dịch nó với cobra filename -turbo

Các xét nghiệm: AMD FX6300 @ 5.1GHz

  • Thử nghiệm 360 * 10000 được sử dụng bởi câu trả lời C chạy trong 365ms (so với 190ms)

  • Bài kiểm tra 4 mục được sử dụng bởi các câu trả lời của Python và Java chạy trong 0,32 Lời (so với 30 Lời, 3 Lời)

  • Bài kiểm tra góc ngẫu nhiên 1000 được sử dụng bởi câu trả lời của Fortran chạy ở tốc độ 100ns mỗi góc (so với 10 Lời)


2
Vì vậy, ngoài việc đưa ra câu trả lời sai và quá chậm, nó có ổn không? :)

@Lembik Bây giờ đã được sửa.
Οurous

4
Bạn có nhận ra rằng về cơ bản bạn chỉ viết cùng một chương trình trong một con rắn khác không?
Ephraim

0

C

Đây là nỗ lực của tôi. Nó hoạt động như thế này:

Xây dựng một bảng gồm tất cả các giá trị của sin (x) từ 0 đến 450 độ. Tương đương, đây là tất cả các giá trị của cos (x) từ -90 đến 360 độ. Với 2926 phần tử, có đủ không gian cho một giá trị cứ sau 1 / 6,5 độ. Do đó, đơn vị chương trình là 1 / 6,5 độ và có 585 đơn vị trong một quý.

Chuyển đổi độ đầu vào thành các đơn vị chương trình (nhân với 6.5==110.1 binary.) Tìm các giá trị gần nhất cho sin và cos từ bảng. sau đó chuyển đổi phần còn lại của đầu vào (dx) thành radian.

áp dụng sin(x+dx) == sin x +(d(sin x)/dx)*dx.lưu ý công thức đó (d(sin x)/dx)==cos x,nhưng chỉ khi chúng ta sử dụng radian.

thật không may, điều đó không đủ chính xác, do đó, một thuật ngữ khác là bắt buộc, dựa trên đạo hàm tiếp theo d2(sin x)/dx2 == -sin x.Điều này cần được nhân với dx*dx/2(không chắc yếu tố 2 đến từ đâu, nhưng nó hoạt động.)

Thực hiện theo các thủ tục tương tự cho cos x, sau đó tính toán tan x == sin x / cos x.

Có khoảng 17 hoạt động điểm nổi ở đây. Điều đó có thể được cải thiện phần nào. Chương trình chứa xây dựng bảng và đầu ra thử nghiệm bằng cách sử dụng các hàm trig gốc, nhưng thuật toán thì không. Tôi sẽ thêm thời gian và chỉnh sửa để tuân thủ các yêu cầu I / O sau này (hy vọng vào cuối tuần này.) Nó phù hợp với đầu ra của hàm gốc ngoại trừ các giá trị rất nhỏ của sin x và cos x, có thể ứng biến tốt hơn so với đầu ra của hàm gốc với một số điều chỉnh.

<#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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.