การคำนวณแบบเร่งด่วน


16

การคำนวณตรีโกณมิติอย่างรวดเร็ว

งานของคุณคือการสร้างโปรแกรมที่สามารถคำนวณไซน์, โคไซน์และแทนเจนต์ของมุมเป็นองศา

กฎระเบียบ

  • ไม่มีฟังก์ชั่นตรีโกณมิติในตัว (ไม่ได้แยกเป็นสองส่วน, โคเซแคนต์และโคแทนเจนต์หากภาษาของคุณมี)
  • คุณอาจใช้ตารางการค้นหา แต่ขนาดรวมของพวกเขาจะต้องไม่เกิน 3000 สมาชิก (สำหรับการดำเนินการทั้งสามรวมกัน) กรุณาทำให้มันอ่านตารางจากไฟล์ (เช่นtrig.lookup) ดังนั้นพวกเขาจึงไม่สับสนรหัส
  • ไม่มีการเข้าถึงเครือข่าย
  • คุณต้องปัดเศษผลลัพธ์ของคุณอย่างถูกต้องตามที่อธิบายไว้ด้านล่าง อย่าใช้พื้นหรือเพดาน
  • คุณอาจใช้วิธีการใด ๆ ในการคำนวณค่าเช่นเศษส่วนต่อเนื่องตราบใดที่มันถูกต้องถึง 7 ตัวเลขที่มีนัยสำคัญ
  • รหัสของคุณต้องสามารถกำหนดเวลาเองได้ ไม่รวมการดำเนินการของไฟล์ I / O จากเวลาของคุณ - ดังนั้นเพียงแค่เวลาฟังก์ชั่นที่ทำตรีโกณมิติและการปัดเศษ
  • ฉันจะต้องสามารถเรียกใช้รหัสของคุณ โปรดโพสต์ลิงก์ไปยังคอมไพเลอร์ / ล่ามที่พร้อมใช้งานได้อย่างอิสระและให้คำแนะนำที่จำเป็นในการรวบรวม / เรียกใช้โค้ด (เช่นตัวเลือกใดที่จะส่งผ่านไปยัง GCC)
  • ช่องโหว่มาตรฐานใช้

รูปแบบอินพุต

  • อ่านจากไฟล์ที่เรียกว่าtrig.inถ้าภาษาของคุณไม่รองรับไฟล์ I / O
  • มุมอยู่ระหว่าง 0 ถึง 360
  • ข้อมูลที่ป้อนจะประกอบด้วยมุมสิบตัวเลขสำคัญในหน่วยทศนิยมคั่นด้วยบรรทัดใหม่ ตัวอย่างเช่น:

90.00000000
74.54390000
175.5000000

รูปแบบผลลัพธ์

  • สำหรับแต่ละมุมที่ให้มาคุณต้องส่งออกไซน์โคไซน์และแทนเจนต์ไปยังตัวเลขสำคัญ 7 ตัวคั่นด้วยช่องว่างในบรรทัดเดียว ใช้ "สัญกรณ์วิทยาศาสตร์" เช่น1.745329E-5สำหรับtan 0.001หรือสำหรับ1.000000E+0sin 90
  • อินฟินิตี้แสดงว่าหรือน่านโดยnยกตัวอย่างเช่นการส่งออกสำหรับควรจะเป็น90.000000001.000000 0.000000 n
  • หากอินพุตเป็นสามมุมที่คั่นด้วยบรรทัดใหม่เอาต์พุตของคุณควรประกอบด้วยสามบรรทัดแต่ละบรรทัดประกอบด้วยไซน์, โคไซน์และแทนเจนต์
  • คุณไม่สามารถส่งออกสิ่งอื่นใด
  • ส่งออกไปยังไฟล์ที่เรียกว่าtrig.outเว้นแต่ภาษาของคุณไม่รองรับไฟล์ I / O

เกณฑ์การให้คะแนน

  • เร็วที่สุดรหัสความท้าทายคือการเขียนโปรแกรมซึ่งคำนวณค่าทั้งสามนี้โดยเร็วที่สุด เวลาที่เร็วที่สุดชนะ
  • ทุกคนจะได้รับอินพุตการทดสอบเดียวกันในหลาย ๆ มุม
  • เวลาจะถูกบันทึกไว้ในเครื่องของฉัน
  • คะแนนของคุณคือค่าเฉลี่ยของการวิ่งสามครั้งในอินพุตเดียวกัน (คุณไม่สามารถบันทึกสิ่งใดระหว่างการวิ่งอย่างชัดเจน)
  • ไม่รวมเวลารวบรวม ความท้าทายนี้เป็นวิธีการที่ใช้มากกว่าภาษา (ถ้ามีคนสามารถชี้ให้ฉันเห็นว่าฉันจะยกเว้นเวลารวบรวมสำหรับภาษาเช่น Java ฉันจะขอบคุณมาก)
  • เครื่องของฉันคือการติดตั้ง Ubuntu 14.04 สถานะของโปรเซสเซอร์อยู่ใน Pastebin (ได้มาจากการทำงานcat /proc/cpuinfo)
  • ฉันจะแก้ไขเวลาของคุณเป็นคำตอบเมื่อฉันทดสอบ

เอาต์พุตต้องอยู่ในบรรทัดเดียวหรือไม่? มันดูน่ารักมากเมื่อมันถูกสร้างด้วยคีย์ Enter ... นอกจากนี้มีวันที่ที่ผู้ชนะถูกเลือกหรือไม่?
เอฟราอิม

@Ephraim คุณหมายถึงอะไรโดยจัดรูปแบบด้วยคีย์ Enter? ไม่ไม่มีวันที่ที่ระบุ ฉันต้องการทดสอบโซลูชันเหล่านี้ทั้งหมด แต่ฉันยังไม่ได้ป้อนข้อมูลการทดสอบ (

@professorfish - ดูผลลัพธ์ในคำตอบของฉัน ทุกคนsin, cosและtanอยู่บนบรรทัดใหม่ ฉันต้องเปลี่ยนมันเพื่อส่งคำตอบไปที่บรรทัดเดียวหรือไม่?
เอฟราอิม

2
@Ephraim รูปแบบผลลัพธ์ไม่สำคัญ (นี่ไม่ใช่โค้ดกอล์ฟ) ตราบใดที่มันส่งออก sin cos และ tan สำหรับทุก ๆ มุมและมันแยกจากกัน

1
เราควรที่จะจับเวลาเฉพาะการคำนวณตรีโกณมิติหรือรวม io ในช่วงเวลาด้วยหรือไม่?
gggg

คำตอบ:


6

Fortran 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) ให้การเพิ่มประสิทธิภาพระดับสูงสุดสำหรับโปรเซสเซอร์ของคุณ

การทดสอบของฉันให้ฉันประมาณ 10 microseconds ต่อการคำนวณเมื่อทดสอบ 1,000 มุมสุ่มโดยใช้ gfortran 4.4 (4.7 หรือ 4.8 มีอยู่ใน repos ของ Ubuntu) และประมาณ 9.5 microseconds ใช้ ifort 12.1 การทดสอบมุมสุ่มเพียง 10 มุมจะส่งผลให้เวลาไม่สามารถกำหนดได้โดยใช้รูทีน Fortran เนื่องจากรูทีนเวลาแม่นยำกับมิลลิวินาทีและคณิตศาสตร์ง่ายบอกว่าควรใช้เวลา 0.100 มิลลิวินาทีในการรันทั้ง 10 หมายเลข


แก้ไขเห็นได้ชัดว่าฉันเป็นจังหวะ IO ซึ่ง (a) ทำให้ระยะเวลานานกว่าที่จำเป็นและ (b) ตรงกันข้ามกับกระสุน # 6 ฉันได้อัปเดตรหัสเพื่อสะท้อนถึงสิ่งนี้แล้ว ฉันยังค้นพบว่าการใช้kind=8จำนวนเต็มกับรูทีนย่อยภายในsystem_clockให้ความแม่นยำระดับไมโครวินาที

ด้วยรหัสที่อัปเดตนี้ตอนนี้ฉันกำลังคำนวณค่าชุดของฟังก์ชันตรีโกณมิติแต่ละชุดในเวลาประมาณ 0.3 microseconds (ตัวเลขนัยสำคัญในตอนท้ายแตกต่างกันไปแบบรัน - รัน - รัน แต่อยู่ใกล้ 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 เช่นกันเนื่องจากคุณอ่านภายในลูป กฎเฉพาะบอกว่าคุณไม่ควรใช้เวลา IO การแก้ไขนี้ทำให้คอมพิวเตอร์ของฉันมีความเร็วมากขึ้น: 0.37 ไมโครวินาทีต่อค่าเทียบกับ 6.94 สำหรับโค้ดที่คุณโพสต์ นอกจากนี้โค้ดที่โพสต์ไม่ได้คอมไพล์มีเครื่องหมายจุลภาคต่อท้ายบรรทัดที่ 100 นอกจากนี้ยังมีข้อผิดพลาดในบรรทัดที่ 23: trigs (i) ควรเป็นเพียง trigs สิ่งนี้ทำให้ segfault รหัสที่โพสต์
- extrinsic

รุ่นที่ปรับปรุงแล้วที่นี่: pastebin.com/freiHTfx
semi-extrinsic

อัปเดตอีกครั้ง: ตัวเลือกคอมไพเลอร์: -march และ -fno-protect-parens ไม่ได้ทำอะไรเลย แต่ -fstack-arrays โกนหนวดออกไปอีก 0.1 microseconds ต่อค่า "ifort -O3 -xHost" นั้นน่าทึ่งช้ากว่า "gfortran -O3 -fstack-arrays" เกือบสองเท่า: 0.55 เทียบกับ 0.27
Semi-extrinsic

2

Python 2.7.x หรือ Java (เลือกของคุณ)

ล่ามหลามฟรีสามารถดาวน์โหลดได้จากที่นี่
ล่ามฟรี Java สามารถดาวน์โหลดได้จากที่นี่

โปรแกรมสามารถรับอินพุตทั้งสองจากไฟล์ที่มีชื่อtrig.inอยู่ในไดเรกทอรีเดียวกันกับไฟล์โปรแกรม อินพุตถูกคั่นด้วยบรรทัดใหม่

ฉันทำสิ่งนี้เป็นงูหลามเพราะ - ฉันชอบงูใหญ่ แต่เนื่องจากฉันต้องการที่จะชนะเช่นกันฉันจึงเขียนมันใหม่ใน java หลังจากนั้น ...

เวอร์ชั่น Python:ฉันมีคอมพิวเตอร์ประมาณ 21 ตัวต่อการทำงานหนึ่งครั้ง ผมได้รับเกี่ยวกับ32μsเมื่อทำงานบน IDEone

Java รุ่น:ฉันได้รับเกี่ยวกับ0.4μsต่อการทำงานบนคอมพิวเตอร์ของฉันและ1.8μs บน IDEone

รายละเอียดคอมพิวเตอร์:

  • Windows 8.1 อัปเดต 1 64 บิตพร้อม Intel Core i7-3632QM - 2.2GHz)

ทดสอบ:

  • เวลาต่อการทำงาน" เป็นครั้งที่สะสมก็จะใช้เวลาในการคำนวณsin, cosและtanทั้งหมดของมุมการป้อนข้อมูล
  • อินพุตทดสอบที่ใช้สำหรับทั้งสองเป็นดังนี้:

    90.00000000  
    74.54390000  
    175.5000000  
    3600000.000  
    


เกี่ยวกับรหัส:
หลักฐานสำหรับโปรแกรมนี้คือการประมาณsinและcosใช้ชื่อพหุนามเทย์เลอร์ของพวกเขากับ 14 คำซึ่งเป็นสิ่งที่ฉันคำนวณมีความจำเป็นต้องมีการประมาณการข้อผิดพลาดน้อยกว่า 1e-8 อย่างไรก็ตามฉันพบว่ามันคำนวณได้เร็วsinกว่าcosจึงตัดสินใจคำนวณแทนcosโดยใช้cos=sqrt(1-sin^2)

ชุด Maclaurin แห่งบาป (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
Junurous

@Ourous - ฉันแก้ไขดังนั้นจึงควรทำงานในทั้งสองภาษา
Ephraim

การcosคำนวณของคุณมากเกินไปฉันจะทำsin(x+90degrees)
Skizz

@Skizz - ในโปรแกรมของฉันฉันใช้คำsinว่าทั้งฟังก์ชั่นและตัวแปร ฉันคิดว่ามันจะเร็วกว่าที่จะไม่ต้องผ่านอะไรบางอย่างไปsin()อีกเป็นครั้งที่สอง แต่ฉันจะเปรียบเทียบทั้งสองเพื่อดูว่าเป็นอย่างนั้นหรือไม่ ความประทับใจของคุณที่copySign()ฟังก์ชั่นนั้นช้ากว่าที่เพิ่มสิ่งต่าง ๆ เช่นในsin()ฟังก์ชั่นของฉันไหม
Ephraim

อาฉันเห็นว่าคุณกำลังทำบาปและคอสในเวลาเดียวกัน ความคิดเห็นของฉันจะใช้ได้จริง ๆ ถ้าคุณทำบาปหรือ cos
Skizz

0

อ็อกเทฟ (หรือ Matlab) & C

กระบวนการสร้างที่ซับซ้อนเล็กน้อย แต่เป็นวิธีที่แปลกใหม่และผลลัพธ์ก็น่ายินดี

วิธีการคือการสร้างพหุนามประมาณกำลังสองสำหรับแต่ละระดับ ดังนั้นดีกรี = [0, 1), ดีกรี = [1, 2), ... , ดีกรี = [359, 360) แต่ละคนจะมีพหุนามต่างกัน

อ็อกเทฟ - ส่วนของอาคาร

คู่เป็นที่เปิดเผยต่อสาธารณชน - download octaveGoogle

นี่เป็นการกำหนดพหุนามกำลังสองที่เหมาะสมที่สุดสำหรับทุก ๆ องศา

บันทึกเป็น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

ส่วนอาคารซี

สิ่งนี้จะแปลงคู่ในรูปแบบข้อความเป็นรูปแบบไบนารีดั้งเดิมในระบบของคุณ

บันทึกเป็น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.1GHz

  • การทดสอบแบบ 360 * 10000 ที่ใช้โดยคำตอบ C นั้นจะทำงานใน 365ms (vs 190ms)

  • การทดสอบ 4 รายการที่ใช้โดยคำตอบ Python และ Java ทำงานใน 0.32 (เทียบกับ 30µs, 3µs)

  • การทดสอบแบบสุ่ม 1,000 มุมที่ใช้โดยคำตอบของ Fortran จะทำงานที่ 100ns ต่อมุม (เทียบกับ 10µs)


2
นอกจากจะให้คำตอบที่ผิดและช้าเกินไปแล้ว :)

@Lembik ตอนนี้ได้รับการแก้ไขแล้ว
Junurous

4
คุณรู้หรือไม่ว่าคุณเพิ่งเขียนโปรแกรมเดียวกันในงูตัวอื่น?
Ephraim

0

นี่คือความพยายามของฉัน มันทำงานได้เช่นนี้:

สร้างตารางค่าทั้งหมดของบาป (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 จุด ที่สามารถปรับปรุงได้บ้าง โปรแกรมประกอบด้วยการสร้างตารางและเอาต์พุตการทดสอบโดยใช้ฟังก์ชันตรีโกณมิติ แต่อัลกอริทึมไม่ ฉันจะเพิ่มเวลาและแก้ไขเพื่อให้สอดคล้องกับความต้องการของ I / O ในภายหลัง (หวังว่าสุดสัปดาห์นี้) มันตรงกับเอาต์พุตของฟังก์ชันเนทิฟยกเว้นค่าขนาดเล็กมากของ sin x และ cos x ซึ่งควรจะดีกว่าเอาท์พุตของฟังก์ชันดั้งเดิม tweaking บางอย่าง

<#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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.