Быстрый Триг Расчет


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).
  • Я отредактирую ваше время в вашем ответе, когда я проверю его.

Есть ли выход есть , чтобы быть на одной линии? Он выглядит так красиво, когда он сформирован с помощью клавиши ввода ... Кроме того, есть ли конкретная дата, в которую выбирается победитель?
Ефрем

@ Ефрем, что ты имеешь в виду под форматированием с помощью клавиши ввода? нет, конкретной даты нет Мне действительно нужно протестировать все эти решения, но я еще не сделал тестовый ввод; (

@professfishfish - смотрите вывод в моем ответе. Каждый sin, cosи tanна новой линии. Нужно ли менять его, чтобы выводить ответы в одну строку?
Ефрем

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

1
Должны ли мы рассчитывать только триггерные вычисления или включать IO в расчет времени?
GGGG

Ответы:


6

Фортран 90

Я использую метод CORDIC с предварительно табулированным массивом из 60 значений arctan (подробности о том, почему это необходимо, см. В вики-статье).

Для этого кода требуется файл trig.inсо всеми значениями в новых строках, которые должны храниться в той же папке, что и исполняемый файл Fortran. Компилируя это,

gfortran -O3 -o file file.f90

где fileимя файла, которое вы ему даете (вероятно SinCosTan.f90, будет самым простым, хотя не обязательно совпадать с именем программы и именем файла). Если у вас есть компилятор Intel, я бы порекомендовал использовать

ifort -O3 -xHost -o file file.f90

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

Мои тестовые прогоны давали мне около 10 микросекунд на расчет при тестировании 1000 случайных углов с использованием gfortran 4.4 (4.7 или 4.8 доступно в репозиториях Ubuntu) и около 9.5 микросекунд с использованием ifort 12.1. Тестирование только 10 случайных углов приведет к неопределенному времени с использованием подпрограмм Фортрана, поскольку подпрограмма синхронизации с точностью до миллисекунды, а простая математика говорит, что для выполнения всех 10 чисел требуется 0,100 миллисекунды.


РЕДАКТИРОВАТЬ Я, видимо, рассчитывал IO, который (а) сделал время больше, чем необходимо, и (б) противоречит пункту № 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-protect-parens -fstack-arrays", чтобы посмотреть, поможет ли это.
полуэкстерьер

Кроме того, вы синхронизируете часть ввода-вывода, так как вы читаете внутри цикла. В правилах конкретно сказано, что вы не должны время IO. Исправление этой ошибки значительно ускорило мой компьютер: 0,37 микросекунды на значение против 6,94 для вашего опубликованного кода. Кроме того, опубликованный код не компилируется, в строке 100 стоит запятая. Также в строке 23 есть ошибка: trigs (i) должны быть просто trigs. Это делает отправленный код segfault.
полу-внешнее

Улучшенная версия здесь: pastebin.com/freiHTfx
полу-посторонний

Обновление re: параметры компилятора: -march и -fno-protect-parens ничего не сделали, но -fstack-массивы сбрасывали еще 0,1 микросекунды на значение. «ifort -O3 -xHost», что примечательно, почти в 2 раза медленнее, чем «gfortran -O3 -fstack-arrays»: 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 Core i7-3632QM - 2,2 ГГц)

Тестовое задание:

  • Время на ход»является суммарным временем, необходимым для расчета sin, cosиtan все углы ввода.
  • Тестовый вход, используемый для обоих, выглядит следующим образом:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


О Кодексе
. Исходным условием для этой программы была оценка sinи cosиспользование их полиномов Тейлора с 14 терминами, что, как я рассчитывал, было необходимо для оценки ошибки менее 1e-8. Однако я обнаружил, что это было быстрее, sinчем рассчитывать cos, поэтому решил вместо этого рассчитать cosс помощьюcos=sqrt(1-sin^2)

Маклаурин серия греха (х) Маклаурин серии 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.
Junurous

@Ourous - я изменил его, поэтому теперь он должен работать на обоих языках.
Ефрем

Вы cosрассчитываете излишне, я бы просто сделалsin(x+90degrees)
Skizz

@Skizz - В моей программе я использую слово sinкак функцию и переменную. Я думал, что было бы быстрее не передавать что-то во sin()второй раз, но я сравню их, чтобы увидеть, так ли это на самом деле. Было ли у вас впечатление, что copySign()функция медленнее, чем сложение, например, в моей sin()функции?
Ефрем

Ах, я вижу, что вы делаете грех и потому одновременно. Мой комментарий действительно будет действительным, если вы делаете грех или потому что.
Skizz

0

Октава (или Матлаб) & C

Немного сложный процесс сборки, но своего рода новый подход и результаты были обнадеживающими.

Подход заключается в создании аппроксимирующих квадратичных полиномов для каждой степени. Таким образом, степень = [0, 1), степень = [1, 2), ..., степень = [359, 360) будет иметь разные полиномы.

Октава - строительная часть

Октава общедоступна - 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 из таблицы. затем преобразуйте оставшуюся часть ввода (дх) в радианы.

Примените 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.