Швидкий розрахунок тригерів


16

Швидкі розрахунки тригонометрії

Ваше завдання - створити програму, яка може обчислити синус, косинус і тангенс кута в градусах.

Правила

  • Жодних вбудованих функцій тригонометрії (навіть секант, сексант і котангенс, якщо їх у вас є мова).
  • Ви можете використовувати таблиці пошуку, але їх загальний розмір не повинен перевищувати 3000 членів (для всіх трьох операцій, узятих разом). Будь ласка, змусьте його читати таблиці з файлу (наприклад trig.lookup), щоб вони не плутали код.
  • Немає доступу до мережі.
  • Ви повинні правильно округлити результат, як пояснено нижче. Не використовуйте підлогу або стелю.
  • Ви можете використовувати будь-який метод для обчислення значень, наприклад, тривалі дроби , якщо це правильно для 7 значущих цифр.
  • Ваш код повинен мати можливість самостійно провести час. Виключіть операції вводу / виводу файлів зі свого часу - значить, лише час функціонування, які виконують (і) запуск і будь-яке округлення.
  • Я повинен бути в змозі запустити ваш код. Будь ласка, опублікуйте посилання на вільно доступний компілятор / інтерпретатор та дайте вказівки, необхідні для компіляції / запуску коду (наприклад, які варіанти передати GCC).
  • Застосовуються стандартні лазівки .

Формат введення

  • Читайте з файлу, який називається, trig.inякщо ваша мова не підтримує введення / виведення файлу.
  • Кути становлять від 0 до 360 включно.
  • Введення складається з кутів до десяти значущих цифр у десяткових цифрах, розділених новими рядками. Наприклад:

90.00000000
74.54390000
175.5000000

Формат виводу

  • Для кожного наданого кута ви повинні вивести його синус, косинус і дотичну до 7 значущих фігур, розділених пробілами, в одному рядку. Використовуйте "наукові позначення", наприклад, 1.745329E-5для tan 0.001або 1.000000E+0для sin 90.
  • Позначимо нескінченність або NaN через n, наприклад, вихід 90.00000000повинен бути 1.000000 0.000000 n.
  • Якщо вхід складається з трьох кутів, розділених новими рядками, то вихід повинен складатися з трьох ліній, кожен з яких містить синус, косинус і дотичну.
  • Ви можете нічого не виводити.
  • Виведіть у файл, який називається, trig.outякщо ваша мова не підтримує введення / виведення файлу.

Оцінка балів

  • . Завдання полягає в тому, щоб написати програму, яка обчислює ці три значення якомога швидше. Виграє найшвидший час.
  • Кожен отримає однаковий тестовий вхід з багатьох кутів.
  • Часи будуть записані на моїй машині.
  • Ваш бал - це середнє значення трьох пробіжок на одному вході (очевидно, що між ними не можна зберегти нічого).
  • Час складання не включається. Це завдання стосується скоріше методу, ніж мови. (Якщо хтось міг би вказати мені на те, як я би виключив час збирання для таких мов, як Java, я буду дуже вдячний)
  • Моя машина - це установка Ubuntu 14.04. Статистика процесора знаходиться на Pastebin (отриманий за допомогою запуску cat /proc/cpuinfo).
  • Я відредагую ваш час у вашій відповіді, коли я її перевіряю.

Чи повинен виведення на одному рядку? Це виглядає так симпатично, коли він оформлений клавішею введення ... Також, є конкретна дата, коли вибирається переможець?
— Єфрем

@Ephraim, що ти маєш на увазі під форматуванням клавіші введення? ні, конкретної дати немає. Мені справді потрібно протестувати всі ці рішення, але я ще не зробив тестовий вклад; (

@professorfish - див. вихід у моїй відповіді. Кожен sin, cosі tanзнаходиться на новій лінії. Чи потрібно мені це змінити, щоб вивести відповіді на один рядок?
— Єфрем

2
@Ephraim Формат виводу насправді не має значення (це не код-гольф), якщо він видає sin cos і загар для кожного кута, і вони є окремими

1
Ми повинні проводити час лише на трійкові обчислення чи включати іо в терміни?
— gggg

Відповіді:


6

Фортран 90

Я використовую метод CORDIC із заздалегідь розробленим масивом з 60 арктанових значень (детальніше про те, чому це необхідно, див. У статті Wiki).

Цей код вимагає trig.inзбереження файлу з усіма значеннями в нових рядках у тій самій папці, що і виконавчий файл Fortran. Складаючи це,

gfortran -O3 -o file file.f90

де fileб ви SinCosTan.f90не назвали ім’я файлу (мабуть, це було б найпростіше, хоча це не обов'язково співставляти назву програми та ім'я файлу). Якщо у вас є компілятор Intel, я рекомендую використовувати

ifort -O3 -xHost -o file file.f90

оскільки -xHost(що не існує для gfortran) забезпечує оптимізацію більш високого рівня, доступну для вашого процесора.

Під час тестування 1000 випадкових кутів за допомогою gfortran 4.4 (4.7 або 4.8 доступно в репортажі Ubuntu) і приблизно 9,5 мікросекунд за допомогою ifort 12.1. Тестування лише 10 випадкових кутів призведе до невизначеного часу з використанням процедур Fortran, оскільки розпорядок часу є точним до мілісекунди, а проста математика говорить, що для запуску всіх 10 чисел потрібно 1000 мілісекунд.


EDIT Мабуть, я був тимчасовим введенням, який (а) зробив терміни довше, ніж було потрібно, і (b) суперечить кулі №6. Я оновив код, щоб це відобразити. Я також виявив, що використання kind=8цілого числа з внутрішньою підпрограмою system_clockдає мікросекундну точність.

За допомогою цього оновленого коду я зараз обчислюю кожен набір значень тригонометричних функцій приблизно за 0,3 мікросекунди (значні цифри в кінцевому підсумку змінюються в режимі "біг-біг", але він постійно зависає поблизу 0,31 нас), що суттєве зменшення від попереднього ітерація, яка приурочила IO.


program SinCosTan
   implicit none
   integer, parameter :: real64 = selected_real_kind(15,307)
   real(real64), parameter :: PI  = 3.1415926535897932384626433832795028842
   real(real64), parameter :: TAU = 6.2831853071795864769252867665590057684
   real(real64), parameter :: half = 0.500000000000000000000_real64
   real(real64), allocatable :: trigs(:,:), angles(:)
   real(real64) :: time(2), times, b
   character(len=12) :: tout
   integer :: i,j,ierr,amax
   integer(kind=8) :: cnt(2)

   open(unit=10,file='trig.out',status='replace')
   open(unit=12,file='CodeGolf/trig.in',status='old')
! check to see how many angles there are
   i=0
   do
      read(12,*,iostat=ierr) b
      if(ierr/=0) exit
      i=i+1
   enddo !- 
   print '(a,i0,a)',"There are ",i," angles"
   amax = i

! allocate array
   allocate(trigs(3,amax),angles(amax))

! rewind the file then read the angles into the array
   rewind(12)
   do i=1,amax
      read(12,*) angles(i)
   enddo !- i

! compute trig functions & time it
   times = 0.0_real64
   call system_clock(cnt(1)) ! <-- system_clock with an 8-bit INT can time to us
   do i=1,amax
      call CORDIC(angles(i), trigs(:,i), 40)
   enddo !- i
   call system_clock(cnt(2))
   times = times + (cnt(2) - cnt(1))

! write the angles to the file
   do i=1,amax
      do j=1,3
         if(trigs(j,i) > 1d100) then
            write(tout,'(a1)') 'n'
         elseif(abs(trigs(j,i)) > 1.0) then
            write(tout,'(f10.6)') trigs(j,i)
         elseif(abs(trigs(j,i)) < 0.1) then
            write(tout,'(f10.8)') trigs(j,i)
         else
            write(tout,'(f9.7)') trigs(j,i)
         endif
         write(10,'(a)',advance='no') tout
      enddo !- j
      write(10,*)" "
   enddo !- i

   print *,"computation took",times/real(i,real64),"us per angle"
   close(10); close(12)
 contains
   !> @brief compute sine/cosine/tangent
   subroutine CORDIC(a,t,n)
     real(real64), intent(in) :: a
     real(real64), intent(inout) :: t(3)
     integer, intent(in) :: n
! local variables
     real(real64), parameter :: deg2rad = 1.745329252e-2
     real(real64), parameter :: angles(60) = &
       [ 7.8539816339744830962e-01_real64, 4.6364760900080611621e-01_real64, &
         2.4497866312686415417e-01_real64, 1.2435499454676143503e-01_real64, &
         6.2418809995957348474e-02_real64, 3.1239833430268276254e-02_real64, &
         1.5623728620476830803e-02_real64, 7.8123410601011112965e-03_real64, &
         3.9062301319669718276e-03_real64, 1.9531225164788186851e-03_real64, &
         9.7656218955931943040e-04_real64, 4.8828121119489827547e-04_real64, &
         2.4414062014936176402e-04_real64, 1.2207031189367020424e-04_real64, &
         6.1035156174208775022e-05_real64, 3.0517578115526096862e-05_real64, &
         1.5258789061315762107e-05_real64, 7.6293945311019702634e-06_real64, &
         3.8146972656064962829e-06_real64, 1.9073486328101870354e-06_real64, &
         9.5367431640596087942e-07_real64, 4.7683715820308885993e-07_real64, &
         2.3841857910155798249e-07_real64, 1.1920928955078068531e-07_real64, &
         5.9604644775390554414e-08_real64, 2.9802322387695303677e-08_real64, &
         1.4901161193847655147e-08_real64, 7.4505805969238279871e-09_real64, &
         3.7252902984619140453e-09_real64, 1.8626451492309570291e-09_real64, &
         9.3132257461547851536e-10_real64, 4.6566128730773925778e-10_real64, &
         2.3283064365386962890e-10_real64, 1.1641532182693481445e-10_real64, &
         5.8207660913467407226e-11_real64, 2.9103830456733703613e-11_real64, &
         1.4551915228366851807e-11_real64, 7.2759576141834259033e-12_real64, &
         3.6379788070917129517e-12_real64, 1.8189894035458564758e-12_real64, &
         9.0949470177292823792e-13_real64, 4.5474735088646411896e-13_real64, &
         2.2737367544323205948e-13_real64, 1.1368683772161602974e-13_real64, &
         5.6843418860808014870e-14_real64, 2.8421709430404007435e-14_real64, &
         1.4210854715202003717e-14_real64, 7.1054273576010018587e-15_real64, &
         3.5527136788005009294e-15_real64, 1.7763568394002504647e-15_real64, &
         8.8817841970012523234e-16_real64, 4.4408920985006261617e-16_real64, &
         2.2204460492503130808e-16_real64, 1.1102230246251565404e-16_real64, &
         5.5511151231257827021e-17_real64, 2.7755575615628913511e-17_real64, &
         1.3877787807814456755e-17_real64, 6.9388939039072283776e-18_real64, &
         3.4694469519536141888e-18_real64, 1.7347234759768070944e-18_real64]
     real(real64), parameter :: kvalues(33) = &
       [ 0.70710678118654752440e+00_real64, 0.63245553203367586640e+00_real64, &
         0.61357199107789634961e+00_real64, 0.60883391251775242102e+00_real64, &
         0.60764825625616820093e+00_real64, 0.60735177014129595905e+00_real64, &
         0.60727764409352599905e+00_real64, 0.60725911229889273006e+00_real64, &
         0.60725447933256232972e+00_real64, 0.60725332108987516334e+00_real64, &
         0.60725303152913433540e+00_real64, 0.60725295913894481363e+00_real64, &
         0.60725294104139716351e+00_real64, 0.60725293651701023413e+00_real64, &
         0.60725293538591350073e+00_real64, 0.60725293510313931731e+00_real64, &
         0.60725293503244577146e+00_real64, 0.60725293501477238499e+00_real64, &
         0.60725293501035403837e+00_real64, 0.60725293500924945172e+00_real64, &
         0.60725293500897330506e+00_real64, 0.60725293500890426839e+00_real64, &
         0.60725293500888700922e+00_real64, 0.60725293500888269443e+00_real64, &
         0.60725293500888161574e+00_real64, 0.60725293500888134606e+00_real64, &
         0.60725293500888127864e+00_real64, 0.60725293500888126179e+00_real64, &
         0.60725293500888125757e+00_real64, 0.60725293500888125652e+00_real64, &
         0.60725293500888125626e+00_real64, 0.60725293500888125619e+00_real64, &
         0.60725293500888125617e+00_real64 ]
    real(real64) :: beta, c, c2, factor, poweroftwo, s
    real(real64) :: s2, sigma, sign_factor, theta, angle
    integer :: j

! scale to radians
    beta = a*deg2rad
! ensure angle is shifted to appropriate range
    call angleShift(beta, -PI, theta)
! check for signs
    if( theta < -half*PI) then
       theta = theta + PI
       sign_factor = -1.0_real64
    else if( half*PI < theta) then
       theta = theta - PI
       sign_factor = -1.0_real64
    else
       sign_factor = +1.0_real64
    endif

! set up some initializations...    
    c = 1.0_real64
    s = 0.0_real64
    poweroftwo = 1.0_real64
    angle = angles(1)

! run for 30 iterations (should be good enough, need testing)
    do j=1,n
       sigma = merge(-1.0_real64, +1.0_real64, theta <  0.0_real64)
       factor = sigma*poweroftwo

       c2 = c - factor*s
       s2 = factor*c + s
       c = c2
       s = s2
! update remaining angle
       theta = theta - sigma*angle

       poweroftwo = poweroftwo*0.5_real64
       if(j+1 > 60) then
          angle = angle * 0.5_real64
       else
          angle = angles(j+1)
       endif
    enddo !- j

    if(n > 0) then
       c = c*Kvalues(min(n,33))
       s = s*Kvalues(min(n,33))
    endif
    c = c*sign_factor
    s = s*sign_factor

    t = [s, c, s/c]
   end subroutine CORDIC

   subroutine angleShift(alpha, beta, gamma)
     real(real64), intent(in) :: alpha, beta
     real(real64), intent(out) :: gamma
     if(alpha < beta) then
        gamma = beta - mod(beta - alpha, TAU) + TAU
     else
        gamma = beta + mod(alpha - beta, TAU) 
     endif
   end subroutine angleShift

end program SinCosTan

2
Нарешті, хтось застосував CORDIC: D
— qwr

1
Я думаю, що "-march = native" - ​​це прапор gfortran, відповідний ifort "-xHost". Крім того, я вважаю, що Intel налаштував -O3 на більш агресивний режим, ніж gfortran, тому ви можете спробувати gfortran з "-O3 -fno-protection-parens -fstack-масиви", щоб побачити, чи допомагає він.
— напів зовнішній

Крім того, ви також розміщуєте частину IO, оскільки ви читаєте всередині циклу. Правила конкретно говорять, що не слід вводити IO. Виправлення цього дало значну швидкість на моєму комп’ютері: 0,37 мікросекунд на значення, проти 6,94 для вашого розміщеного коду. Крім того, розміщений код не компілюється, в рядку 100 є кінцева кома. Також у рядку 23 є помилка: тригги (i) повинні бути просто триггами. Це робить розміщений код по умолчанию.
— напів зовнішній

Тут вдосконалена версія: pastebin.com/freiHTfx
— напівекстернічний

Оновити параметри компілятора re: -march та -fno-protection-parens нічого не зробили, але -fstack-масиви відсіли ще 0,1 мікросекунди за значення. "ifort -O3 -xHost", дивовижно, майже в 2 рази повільніше, ніж "gfortran -O3 -fstack-масиви": 0,55 проти 0,27
— напівекстернічний

2

Python 2.7.x або Java (Беріть свій вибір)

Безкоштовний перекладач Python можна завантажити тут . Звідси
можна завантажити безкоштовний перекладач Java .

Програма може приймати дані як з файлу з ім'ям, trig.inрозташованого в тому самому каталозі, що і програмного файлу. Введення розділено новими рядками.

Я спочатку робив це в python, тому що - ну, я люблю python. Але, оскільки я також хочу спробувати перемогти, я переписав її в Java потім ...

Версія Python: На моєму комп’ютері я отримував близько 21 мкс за пробіг. Я отримав близько 32 мкс під час запуску на IDEone .

Версія Java: на моєму комп’ютері я отримую близько 0,4 мкс за пробіг та 1,8 мкс на IDEone .

Технічні характеристики комп'ютера:

  • Оновлення Windows 8.1 1 64-бітне з ядром Intel i7-3632QM - 2,2 ГГц)

Тест:

  • Час на ходу »є сумарний час, необхідне для розрахунку sin, cosі tanвсі кути введення.
  • Тестовий вхід для обох використовується наступним чином:

    90,00000000  
    74,54390000  
    175.5000000  
    3600000.000  
    


Про кодекс:
Попередження цієї програми полягало в тому, щоб оцінити sinі cosвикористовувати їхні многочлени Тейлора з 14 членами, і це було те, що, за моїми підрахунками, було необхідним, щоб оцінка помилок становила менше 1е-8. Однак я знайшов, що швидше обчислити, sinніж cos, тому вирішив замість цього обчислити 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 - я змінив його, тому він повинен працювати зараз на обох мовах.
— Єфрем

Ваш cosрозрахунок є надмірним, я б тільки зробивsin(x+90degrees)
— Skizz

@Skizz - У своїй програмі я використовую слово sinяк функцію, так і змінну. Я подумав, що швидше не доведеться щось передавати sin()вдруге, але я порівняю два, щоб побачити, чи справді це так. Чи було у вас враження, що copySign()функція повільніше, ніж додавання таких речей, як у моїй sin()функції?
— Єфрем

Ах, я бачу, ти робиш гріх і союз одночасно. Мій коментар був би дійсно справедливим лише в тому випадку, якщо ви робите гріх або сотвор.
— Скізз

0

Октава (або Матлаб) і С

Трохи складний процес збирання, але якийсь новий підхід та результати були обнадійливими.

Підхід полягає у формуванні наближених квадратичних многочленів для кожного ступеня. Отже ступінь = [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 - будівельна частина

Це перетворює подвоєння у текстовому форматі у нативний бінарний формат у вашій системі.

Зберегти як build-fast-trig.c:

#include <stdio.h>

int main()
{
    double d[3];

    while (scanf("%lf %lf %lf", d, d + 1, d + 2) == 3)
        fwrite(d, sizeof(double), 3, stdout);

    return 0;
}

Збірка:

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

Генерування файлу коефіцієнтів

Виконати:

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

Тепер у нас є qcoeffs.datфайл даних, який потрібно використовувати для фактичної програми.

C - швидкодіюча частина

Зберегти як fast-trig.c:

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#define INPUT    "qcoeffs.dat"

#define DEGREES    360

typedef struct {double a, b, c;} QCOEFFS;

double normalize(double d)
{
    if (d < 0.0)
        d += ceil(d / -(double)DEGREES) * (double)DEGREES;

    if (d >= (double)DEGREES)
        d -= floor(d / (double)DEGREES) * (double)DEGREES;

    return d;
}

int main()
{
    FILE *f;
    time_t tm;
    double d;
    QCOEFFS qc[DEGREES];

    if (!(f = fopen(INPUT, "rb")) || fread(qc, sizeof(QCOEFFS), DEGREES, f) < DEGREES)
    {
        fprintf(stderr, "Problem with %s - aborting.", INPUT);
        return EXIT_FAILURE;
    }
    fclose(f);

    tm = -clock();

    while (scanf("%lf", &d) > 0)
    {
        int i;
        double e, f;

        /* sin */
        d = normalize(d);
        i = (int)d;
        e = (qc[i].a * d + qc[i].b) * d + qc[i].c;

        /* cos */
        d = normalize((double)DEGREES / 4.0 - d);
        i = (int)d;
        f = (qc[i].a * d + qc[i].b) * d + qc[i].c;

        /* tan = sin / cos */

        /* output - format closest to specs, AFAICT */
        if (d != 0.0 && d != 180.0)
            printf("%.6e %.6e %.6e\n", e, f, e / f);
        else
            printf("%.6e %.6e n\n", e, f);
    }

    tm += clock();

    fprintf(stderr, "time: %.3fs\n", (double)tm/(double)CLOCKS_PER_SEC);    

    return EXIT_SUCCESS;
}

Збірка:

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

Виконати:

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

Він буде читати trig.in, зберігати trig.outта друкувати, щоб втішити минулий час з мілісекундною точністю.

Залежно від використовуваних методів тестування, він може вийти з ладу на певному вході, наприклад:

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

Правильний вихід повинен бути 0.000000e+00 1.000000e+00 0.000000e+00. Якщо результати перевіряються за допомогою рядків, введення не вдасться, якщо вони перевірені за допомогою абсолютної помилки, наприклад fabs(actual - result) < 1e-06, вхід пройде.

Максимальна абсолютна похибка для sinта cosстановила ≤ 3e-07. Для tan, так як результат не обмежується ± 1 , і ви можете розділити відносно велике число відносно невеликого числа, абсолютна похибка може бути більше. З -1 ≤ tan (x) ≤ +1, максимальна абсолютна похибка склала ≤ 4e-07. Для tan (x)> 1 і tan (x) <-1 максимальна відносна похибка, наприклад, fabs((actual - result) / actual)зазвичай була <1e-06, поки ви не потрапите в область (90 ± 5) або (270 ± 5) градусів, то помилка посилюється.

У тестуванні, середній час на один вхід був (1,053 ± 0,007) мкс, який на моїй машині було близько 0.070 мкс швидше , ніж рідний sinі cos, tanвизначається таким же чином.


0

Кобра

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

Скомпілюйте його cobra filename -turbo

Тести: AMD FX6300 @ 5,1 ГГц

  • Тест 360 * 10000, який використовується відповіді C, працює за 365 мс (проти 190 мс)

  • Тест з 4 входами, який використовується відповідями Python та Java, працює за 0,32 мкс (проти 30 мкс, 3 мкс)

  • Тест 1000 випадкового кута, який використовується відповіді Фортран, працює зі швидкістю 100 нс на кут (проти 10 мкс)


2
Тож крім того, щоб дати неправильну відповідь і бути занадто повільним, це добре? :)

@Lembik Це тепер виправлено.
— Οurous

4
ти розумієш, що в основному ти просто написав ту саму програму в іншій змії?
— Єфрем

0

С

Ось моя спроба. Це працює так:

Побудуйте таблицю всіх значень sin (x) від 0 до 450 градусів. Рівнозначно це всі значення 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 операцій з плаваючою точкою. Це можна дещо покращити. Програма містить побудову таблиць і тестовий вихід з використанням нативної функції триггеру, але алгоритм цього не робить. Я додаю терміни та редагування, щоб відповідати вимогам вводу / виводу пізніше (сподіваємось, у ці вихідні). Він відповідає основній функції виходу, за винятком дуже малих значень sin x та cos x, які повинні бути незмінними для кращого, ніж вихідний функція з деяке налаштування.

<#include <math.h>                                                 //only for table building and testing
int a;
double t[2926],                                                    //a table for sin x from 0 to 360+90=450deg every 1/6.5 degrees
x,                                                                 //input
s,c,                                                               //first guess sin and cos (from table)
sn,cs,                                                             //output sin and cos
pi1170=3.1415926535897932384626433832795/1170,                     // there are 1170 units of 1/6.5 degrees in a half circle 
pi180=3.1415926535897932384626433832795/180;                       // pi/180 only used for testing

main(){
  for (a=0;a<=2925;a++)t[a]=sin(a*pi1170);                         //build table. 

  scanf("%lf",&x);                                                 //input 
  printf("%le %le %le\n",sin(x*pi180),cos(x*pi180),tan(x*pi180));  //native output for testing purposes

  x*=6.5;                                                          //convert from deg to program units. 6.5=110.1 in binary, a fairly round number. 
  a=x+0.5;                                                         //a=round(x) add 0.5 to round, not truncate. Assigning to int, this is probably faster than the round function.
  x=(x-a)*pi1170;                                                  //(x-a)=dx in program units. Divide to get radians. 

  s=t[a];                                                          //get sin(a) from table
  c=t[a+585];                                                      //cos(a)=sin(a+90degrees)=sin(a+585units)
  sn=s+c*x-s*x*x/2;                                                //sin(x+dx)=sin(x)+cos(dx)-sin(dx^2/2)
  cs=c-s*x-c*x*x/2;                                                //cos(x+dx)=cos(x)-sin(dx)-cos(dx^2/2)
  printf("%le %le %le\n",sn,cs,sn/cs);                             //print sin,cos and tan=sin/cos
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.