如果有人發現截至2019.1.30之前的評論和本文內容對不上,那是因為該日之前演算法有過一次大的更新,以至於本文應該被重寫。目前的內容是2019.1.30重寫的以及此後在此基礎之上進行的非顯著修改的結果。此前的版本見如下備份。

astrojhgu/adaptrapezoid_benchmark?

github.com圖標

o/o/o/o/o/o/o/o/以o/o/o/o/o/o/o/o/

o/o/o/o/o/o/o/o/下o/o/o/o/o/o/o/o/

o/o/o/o/o/o/o/o/是o/o/o/o/o/o/o/o/

o/o/o/o/o/o/o/o/正o/o/o/o/o/o/o/o/

o/o/o/o/o/o/o/o/文o/o/o/o/o/o/o/o/

動因

通過實際問題,比較若干潛在適合於科學計算的編程語言的運行速度。

比較的原則

確保演算法一致:

儘可能確保在偽代碼相同的條件下進行比較,避免演算法的選擇對比較結果的影響。例如,本文中所選用的演算法是自適應梯形積分法。在這一前提下,用不同的語言實現這一演算法,並對同一個被積函數,在相同的精度要求下進行問題求解,並比較得到結果所需要的時間。毫無疑問,存在精度收斂速度更快的演算法,例如辛普森方法、高斯積分法等等,但是作者不希望演算法本身的速度差異被引入,因此所有的比較都將確保採用同一種演算法。

偽代碼儘可能保持一致:

即使是相同的演算法,在實現過程中,也可以存在不同的偽代碼。例如在此前的一個版本中,作者選用了一種保存了過多冗餘信息的偽代碼。本文儘可能確保所選用的偽代碼邏輯結構貼近實用代碼,並在不同語言之間保持一致。

例外:

某些語言(Haskell),並不適合採用類C語言中基於迭代的演算法來編寫,因此不得不採用基於遞歸的演算法。

用於比較的問題

問題描述:

求如下定積分

int_0^sqrt{8pi}sin(x^2)dx

為什麼選取這個函數( sin(x^2) )作為被積函數?

因為這個函數結構豐富,各階導數均不全局為0,對積分演算法有一定的要求。

實現偽代碼

演算法1(總體演算法)

  1. 設定被積函數F、積分精度 epsilon .
  2. 初始化一系列初始分割點, 初始分割點的x坐標依次遞增。這n個分割點對應與n-1個子積分區間。例如對於分割點 x_i,( i=0..n-1) 對應的初始積分區間為 [x_0, x_1], [x_1, x_2], cdots [x_{n-2}, x_{n-1}]
  3. 對於任何積分區間 [a,b] 上的積0分結果,用函數 I{F, a, b} 加以計算, 函數 I 的定義見演算法2。
  4. 將每一個積分區間上的積分結果按照絕對值升序排列,並從前到後求和,得到最終積分結果。

演算法2(對於 I 的定義)

  1. 計算偏差 delta=T{F, a, b}-T{F, a, frac{a+b}{2}}-T{F, frac{a+b}{2}, b} ,其中 T{F, a, b}=frac{b-a}{2}(F(a)+F(b))
  2. 如果 |delta|ltfrac{epsilon}{W}(b-a) , go to 3, 否則 go to ;其中W是原始的積分區間.
  3. 返回  T{F, a, (a+b)/2}+T{F, (a+b)/2,b)
  4. 返回 I{F, a, (a+b)/2)+I{F, (a+b)/2, b)

對於強調函數式編程風格的語言(例如Haskell),將採用遞歸方式實現,對於強調指令式編程風格,但沒有顯式尾遞歸的語言(例如C++、Rust、Python、Julia),將採用模擬遞歸方式。對於有尾遞歸的語言(例如Scala),將盡量用尾遞歸和模擬遞歸方式實現(取決於我有沒有時間)。

代碼

本文所涉及的代碼見

astrojhgu/adaptrapezoid_benchmark?

github.com
圖標

目錄

對於各個語言的實現和結果,見下(待續)。


推薦閱讀:
相關文章