Perhitungan Trig Cepat


16

Perhitungan Trigonometri Cepat

Tugas Anda adalah membuat program yang dapat menghitung sinus, kosinus dan garis singgung dari sudut dalam derajat.

Aturan

  • Tidak ada fungsi trigonometri bawaan (bahkan tidak secant, cosecant dan cotangent jika bahasa Anda memilikinya).
  • Anda dapat menggunakan tabel pencarian, tetapi ukuran totalnya tidak boleh lebih dari 3000 anggota (untuk ketiga operasi disatukan). Tolong buat itu membaca tabel dari file (misalnya trig.lookup) sehingga mereka tidak membingungkan kode.
  • Tidak ada akses jaringan.
  • Anda harus membulatkan output dengan benar seperti dijelaskan di bawah ini. Jangan gunakan lantai atau langit-langit.
  • Anda dapat menggunakan metode apa pun untuk menghitung nilai, misalnya pecahan lanjutan , asalkan benar ke 7 angka penting.
  • Kode Anda harus dapat menentukan waktu sendiri. Kecualikan operasi file I / O dari waktu Anda - jadi tentukan saja fungsi yang melakukan trigonometri dan pembulatan apa pun.
  • Saya harus dapat menjalankan kode Anda. Silakan kirim tautan ke kompiler / juru bahasa yang tersedia secara bebas dan berikan instruksi yang diperlukan untuk mengkompilasi / menjalankan kode (mis. Opsi apa yang harus diteruskan ke GCC).
  • Celah standar berlaku.

Masukkan format

  • Baca dari file yang dipanggil trig.inkecuali bahasa Anda tidak mendukung file I / O.
  • Sudutnya antara 0 dan 360 inklusif.
  • Input akan terdiri dari sudut hingga sepuluh angka signifikan dalam digit desimal, dipisahkan oleh garis baru. Sebagai contoh:

90.00000000
74.54390000
175.5000000

Format output

  • Untuk setiap sudut yang disediakan, Anda harus menampilkan sinus, kosinus, dan garis singgung ke 7 angka signifikan, dipisahkan oleh spasi, pada satu baris. Gunakan "notasi ilmiah", misalnya 1.745329E-5untuk tan 0.001atau 1.000000E+0untuk sin 90.
  • Nyatakan tak terhingga atau NaN oleh n, misalnya output untuk 90.00000000seharusnya 1.000000 0.000000 n.
  • Jika input adalah tiga sudut yang dipisahkan oleh baris baru, output Anda harus terdiri dari tiga baris, masing-masing berisi sinus, kosinus dan garis singgung.
  • Anda mungkin tidak menghasilkan apa pun.
  • Keluaran ke file yang dipanggil trig.outkecuali bahasa Anda tidak mendukung file I / O.

Mencetak gol

  • . Tantangannya adalah menulis sebuah program yang menghitung tiga nilai ini secepat mungkin. Waktu tercepat menang.
  • Setiap orang akan menerima input tes yang sama dari banyak sudut.
  • Waktu akan direkam di mesin saya.
  • Skor Anda adalah rata-rata dari tiga proses pada input yang sama (Anda tidak dapat menyimpan apa pun di antara proses yang jelas).
  • Waktu kompilasi tidak termasuk. Tantangan ini lebih tentang metode yang digunakan daripada bahasa. (Jika seseorang bisa mengarahkan saya ke bagaimana saya akan mengecualikan waktu kompilasi untuk bahasa seperti Java, saya akan sangat berterima kasih)
  • Mesin saya adalah instalasi Ubuntu 14.04. Statistik prosesor ada di Pastebin (diperoleh dengan menjalankancat /proc/cpuinfo ).
  • Saya akan mengedit waktu Anda menjadi jawaban Anda ketika saya sudah mengujinya.

Apakah output harus dalam satu baris? Terlihat sangat cantik ketika diformat dengan tombol enter ... Juga, apakah ada tanggal tertentu saat pemenang dipilih?
Ephraim

@Ephraim apa yang Anda maksud dengan diformat dengan kunci enter? tidak, tidak ada tanggal tertentu. Saya benar-benar perlu menguji semua solusi ini, tetapi saya belum membuat input tes; (

@professorfish - lihat output dalam jawaban saya. Setiap sin, cosdan tanada di jalur baru. Apakah saya perlu mengubahnya untuk menampilkan jawaban ke satu baris?
Ephraim

2
@Ephraim Format output benar-benar tidak masalah (ini bukan kode-golf) asalkan menampilkan cos cos dan tan untuk setiap sudut dan mereka terpisah

1
Apakah kita seharusnya menghitung waktu hanya perhitungan trigonometri, atau memasukkan io dalam waktunya?
gggg

Jawaban:


6

Fortran 90

Saya menggunakan metode CORDIC dengan array pra-tabulasi dari 60 nilai arctan (lihat artikel Wiki untuk detail tentang mengapa itu perlu).

Kode ini membutuhkan file,, trig.indengan semua nilai pada baris baru untuk disimpan dalam folder yang sama dengan yang dapat dieksekusi Fortran. Kompilasi ini adalah,

gfortran -O3 -o file file.f90

di mana filenama file apa pun yang Anda berikan (mungkin SinCosTan.f90akan lebih mudah, meskipun tidak perlu mencocokkan nama program dan nama file). Jika Anda memiliki kompiler Intel, saya sarankan menggunakan

ifort -O3 -xHost -o file file.f90

sebagai -xHost(yang tidak ada untuk gfortran) memberikan optimasi tingkat yang lebih tinggi tersedia untuk prosesor Anda.

Uji coba saya memberi saya sekitar 10 mikrodetik per perhitungan saat menguji 1000 sudut acak menggunakan gfortran 4.4 (4,7 atau 4,8 tersedia di repo Ubuntu) dan sekitar 9,5 mikrodetik menggunakan ifort 12.1. Menguji hanya 10 sudut acak akan menghasilkan waktu yang tidak dapat ditentukan dengan menggunakan rutinitas Fortran, karena rutinitas waktu akurat untuk milidetik dan matematika sederhana mengatakan perlu 0,100 milidetik untuk menjalankan semua 10 angka.


EDIT Rupanya saya adalah timing IO yang (a) membuat timing lebih lama dari yang diperlukan dan (b) bertentangan dengan bullet # 6. Saya telah memperbarui kode untuk mencerminkan hal ini. Saya juga menemukan bahwa menggunakan kind=8integer dengan subrutin intrinsik system_clockmemberikan akurasi mikrodetik.

Dengan kode yang diperbarui ini, saya sekarang menghitung setiap set nilai fungsi trigonometri dalam sekitar 0,3 mikrodetik (angka signifikan pada akhirnya bervariasi run-to-run, tetapi secara konsisten melayang di dekat 0,31 us), pengurangan yang signifikan dari sebelumnya iterasi yang menghitung waktu 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
Akhirnya, seseorang menggunakan CORDIC: D
qwr

1
Saya pikir "-march = asli" adalah bendera gfortran yang sesuai dengan ifort "-xHost". Juga, saya percaya Intel telah menetapkan -O3 ke mode yang lebih agresif daripada gfortran, jadi Anda dapat mencoba gfortran dengan "-O3 -fno-protect-parens -fstack-array" untuk melihat apakah itu membantu.
semi-ekstrinsik

Juga, Anda mengatur waktu bagian IO juga, karena Anda membaca di dalam loop. Aturan khusus mengatakan Anda tidak boleh mengatur waktu IO. Memperbaiki ini memberi cukup speedup di komputer saya: 0,37 mikrodetik per nilai, vs 6,94 untuk kode Anda diposting. Juga, kode yang diposting tidak mengkompilasi, ada koma tertinggal di baris 100. Ada juga kesalahan di baris 23: trigs (i) harusnya hanyalah trigs. Ini membuat kode yang diposting menjadi segfault.
semi-ekstrinsik

Versi yang ditingkatkan di sini: pastebin.com/freiHTfx
semi-ekstrinsik

Perbarui kembali: opsi kompiler: -march dan -fno-protect-parens tidak melakukan apa-apa, tetapi -fstack-array mengurangi 0,1 mikrodetik per nilai. "ifort -O3 -xHost", luar biasa, hampir 2x lebih lambat dari "gfortran -O3 -fstack-arrays": 0,55 vs 0,27
semi-ekstrinsik

2

Python 2.7.x atau Java (Silakan pilih)

Juru bahasa Python gratis dapat diunduh dari sini .
Penerjemah Java gratis dapat diunduh dari sini .

Program dapat mengambil input dari file bernama trig.interletak di direktori yang sama dengan file program. Input dipisahkan oleh baris baru.

Saya awalnya melakukan ini dengan python karena - yah, saya suka python. Tapi, karena saya ingin mencoba untuk menang juga, saya menulis ulang di java setelahnya ...

Versi Python: Saya mendapat sekitar 21 μs per dijalankan di komputer saya. Saya mendapat sekitar 32 μs ketika menjalankannya di IDEone .

Versi Java: Saya mendapatkan sekitar 0,4 μs per dijalankan di komputer saya, dan 1,8 μs pada IDEone .

Spesifikasi komputer:

  • Pembaruan Windows 8.1 1 64-bit dengan intel core i7-3632QM - 2.2GHz)

Uji:

  • Waktu per run" adalah waktu kumulatif yang diperlukan untuk menghitung sin, cosdan tansemua sudut masukan.
  • Input tes yang digunakan untuk keduanya adalah sebagai berikut:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


Tentang Kode:
Premis untuk program ini adalah untuk memperkirakan sindan cosmenggunakan polinomial Taylor mereka dengan 14 istilah, yang menurut saya perlu untuk memiliki perkiraan kesalahan kurang dari 1e-8. Namun saya menemukan itu lebih cepat untuk menghitung sindaripada cos, jadi saya memutuskan untuk menghitung cosdengan menggunakancos=sqrt(1-sin^2)

Rangkaian dosa Maclaurin (x) Rangkaian Maclaurin cos (x)


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


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

Kosinus Anda salah untuk 180 <x <360, dan program ini gagal total pada 270.
Οurous

@Ourous - Saya memodifikasinya, jadi itu seharusnya berfungsi sekarang dalam kedua bahasa.
Ephraim

cosPerhitungan Anda berlebihan, saya hanya akan lakukansin(x+90degrees)
Skizz

@ Skizz - Dalam program saya, saya menggunakan kata sinsebagai fungsi, dan variabel. Saya pikir akan lebih cepat untuk tidak harus melewati sesuatu untuk yang sin()kedua kalinya, tetapi saya akan membandingkan keduanya untuk melihat apakah itu benar-benar terjadi. Apakah kesan Anda bahwa copySign()fungsi lebih lambat sehingga menambahkan hal-hal seperti dalam sin()fungsi saya ?
Ephraim

Ah, saya melihat Anda melakukan dosa dan cos pada saat yang sama. Komentar saya hanya akan benar-benar valid jika Anda melakukan dosa atau cos.
Skizz

0

Oktaf (atau Matlab) & C

Sedikit proses pembangunan yang rumit, tetapi semacam pendekatan baru dan hasilnya menggembirakan.

Pendekatannya adalah untuk menghasilkan polinomial kuadratik yang mendekati untuk setiap tingkat. Jadi derajat = [0, 1), derajat = [1, 2), ..., derajat = [359, 360) masing-masing akan memiliki polinomial yang berbeda.

Bagian bangunan oktaf

Oktaf tersedia untuk umum - Google download octave.

Ini menentukan polinomial kuadratik paling cocok untuk setiap derajat.

Simpan sebagai 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 - bagian bangunan

Ini mengkonversi ganda dalam format teks ke format biner asli pada sistem Anda.

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

Menyusun:

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

Menghasilkan file koefisien

Lari:

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

Sekarang kita memiliki qcoeffs.datfile data yang akan digunakan untuk program yang sebenarnya.

C - bagian trigonometri cepat

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

Menyusun:

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

Lari:

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

Ini akan membaca dari trig.in, simpan ke trig.outdan cetak untuk menghibur waktu yang telah berlalu dengan presisi milidetik.

Bergantung pada metode pengujian yang digunakan, mungkin gagal pada input tertentu, misalnya:

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

Output yang benar seharusnya 0.000000e+00 1.000000e+00 0.000000e+00. Jika hasilnya divalidasi menggunakan string, input akan gagal, jika divalidasi menggunakan kesalahan absolut, misalnya fabs(actual - result) < 1e-06, input akan diteruskan.

Kesalahan absolut maksimum untuk sindan cosadalah ≤ 3e-07. Karena tan, karena hasilnya tidak terbatas pada ± 1 dan Anda dapat membagi angka yang relatif besar dengan angka yang relatif kecil, kesalahan absolut bisa lebih besar. Dari -1 ≤ tan (x) ≤ +1, kesalahan absolut maksimum adalah ≤ 4e-07. Untuk tan (x)> 1 dan tan (x) <-1, kesalahan relatif maksimum , misalnya fabs((actual - result) / actual)biasanya <1e-06 sampai Anda mendapatkan area (90 ± 5) atau (270 ± 5) derajat, maka kesalahan bertambah buruk.

Dalam pengujian, waktu rata-rata per input tunggal adalah (1,053 ± 0,007) μs, yang pada mesin saya sekitar 0,070 μs lebih cepat daripada asli sindancos , tandidefinisikan dengan cara yang sama.


0

Kobra

class Trig
    const mod as float = 0.0174532925199433f #0.0174532925199432957692369076848861271344287188854172f
    var time as System.Diagnostics.Stopwatch = System.Diagnostics.Stopwatch()
    var file as String[] = File.readAllLines('trig.in')
    var sin_out as float[] = float[](1)
    var cos_out as float[] = float[](1)
    var tan_out as float[] = float[](1)
    def main
        .compute(@[1f])
        .time.reset
        input = float[](.file.length)
        for num, line in .file.numbered, input[num] = float.parse(line)
        .compute(input)
        for num in .file.length, .file[num] = (.sin_out[num].toString('0.000000E+0') + ' ' + .cos_out[num].toString('0.000000E+0') + ' ' + .tan_out[num].toString('0.000000E+0'))
        File.writeAllLines('trig.out', .file)
        print .time.elapsed
    def compute(list as float[])
        .sin_out = float[](list.length)
        .cos_out = float[](list.length)
        .tan_out = float[](list.length)
        .time.start
        for index in list.length
            degrees as float = list[index]
            #add `degrees %= 360` for numbers greater than 360
            rad as float = sin as float = degrees * .mod
            two as float = rad * rad
            sin -= (rad *= two) / 6
            sin += (rad *= two) / 120
            sin -= (rad *= two) / 5040
            sin += (rad *= two) / 362880
            sin -= (rad *= two) / 39916800
            sin += (rad *= two) / 6227020800
            sin -= (rad *= two) / 1307674368000
            sin += (rad *= two) / 355687428096000
            sin -= (rad *= two) / 121645100408832000
            sin += (rad *= two) / 51090942171709440000f
            sin -= (rad *= two) / 25852016738884976640000f
            sin += (rad *= two) / 15511210043330985984000000f
            sin -= (rad *= two) / 10888869450418352160768000000f
            sin += (rad *= two) / 8841761993739701954543616000000f
            cos as float = (1 - (sin * sin)).sqrt * ((degrees - 180).abs - 90).sign
            if cos.isNaN, cos = 0
            .tan_out[index] = Math.round((sin / cos) * 10000000) / 10000000
            .sin_out[index] = Math.round(sin * 10000000) / 10000000
            .cos_out[index] = Math.round(cos * 10000000) / 10000000
        .time.stop

Kompilasi dengan cobra filename -turbo

Pengujian: AMD FX6300 @ 5.1GHz

  • Tes 360 * 10000 yang digunakan oleh jawaban C berjalan dalam 365ms (vs 190ms)

  • Tes 4-entri yang digunakan oleh Python dan Java jawaban berjalan dalam 0,32 μs (vs 30 μs, 3 μs)

  • Uji sudut acak 1000 yang digunakan oleh jawaban Fortran berjalan pada 100ns per sudut (vs 10μs)


2
Jadi selain memberikan jawaban yang salah dan terlalu lambat, tidak apa-apa? :)

@Lembik Sekarang sudah diperbaiki.
Kamis

4
apakah Anda sadar bahwa pada dasarnya Anda hanya menulis program yang sama pada ular yang berbeda?
Ephraim

0

C

Inilah usaha saya. Ini berfungsi seperti ini:

Bangun tabel semua nilai dosa (x) dari 0 hingga 450 derajat. Secara setara, ini semua nilai cos (x) dari -90 hingga 360 derajat. Dengan 2926 elemen, ada ruang yang cukup untuk nilai setiap 1 / 6,5 derajat. Oleh karena itu unit program 1 / 6,5 derajat, dan ada 585 unit dalam seperempat putaran.

Ubah derajat input menjadi unit program (dikalikan dengan 6.5==110.1 binary. ) Temukan nilai terdekat untuk sin dan cos dari tabel. kemudian ubah sisa bagian input (dx) menjadi radian.

terapkan formula sin(x+dx) == sin x +(d(sin x)/dx)*dx.perhatikan itu(d(sin x)/dx)==cos x, tetapi hanya jika kita menggunakan radian.

sayangnya itu tidak cukup akurat dengan sendirinya, jadi diperlukan istilah lain, berdasarkan turunan berikutnya. d2(sin x)/dx2 == -sin x.Ini perlu dikalikan dengan dx*dx/2(tidak yakin dari mana faktor 2 berasal, tetapi berfungsi.)

Ikuti prosedur analog untuk cos x, lalu hitung tan x == sin x / cos x.

Kode

Ada sekitar 17 operasi floating point di sini. Itu bisa diperbaiki sedikit. Program ini berisi pembuatan tabel dan hasil tes menggunakan fungsi trigonometri asli, tetapi algoritma tidak. Saya akan menambahkan waktu dan edit untuk memenuhi persyaratan I / O nanti (mudah-mudahan akhir pekan ini.) Ini cocok dengan output fungsi asli kecuali untuk nilai yang sangat kecil dari sin x dan cos x, yang harus diperbaiki menjadi lebih baik daripada output fungsi asli dengan beberapa penyesuaian.

<#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
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.