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


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.