2017-10-17 61 views
1

的多個I需要計算有效的像f(i,a) = exp(-0.5 * (i-1) * i * a)陣列針對所有i in (0..n),與n高達20.000和a正值非常接近於0效率,但仍然精確的,指數

爲了避免計算exp N次,我用了一個增量的方法,如(Scala中寫):

def fInc(n: Int, a: Double) 
    val expA = Math.exp(-a) 
    var u = 1.0 
    var v = 1.0 

    var i = 1 
    while(i < n){ 
    u *= expA 
    v *= u // in practice I store that value in an array, for all i 
    i += 1 
    } 
} 

// reference by calling exp directly 
def fRef(n: Int, a: Double) = Math.exp(-0.5 * (i-1) * i * a) 

這在數學上是正確的,但隨後直接EXP計算的差異太大。下面是一些結果:

n  a   v    Math.exp    diff 
1000 1E-6 0.6068340008761639 0.6068340008714599 4.704014955336788E-12 
1000 1E-9 0.9995006247427483 0.9995006247293567 1.339151E-11 
1000 1E-12 0.9999995005111699 0.9999995005001248 1.1045164782785832E-11 
1000 1E-15 0.9999999995008992 0.9999999995005  3.992361996552063E-13 
10000 1E-6 1.938417748402E-22 1.938417746809E-22 1.5929953847004499E-31 
10000 1E-9 0.9512341819777599 0.9512341806597269 1.3180330160622589E-9 
10000 1E-12 0.9999500073554776 0.9999500062497292 1.1057483817467073E-9 
10000 1E-15 0.9999999500449599 0.9999999500050013 3.995859199079632E-11 

正如你所看到的,對於一些價值,差異上升到1E-9,而我也許可以接受1E-13

所以問題:

  • 有沒有一種方法可以獲得更好的近似算法,這個算法比調用所有的i上的exp更有效率?

注:

  1. 我使用Apache FastMath EXP,這給了幾乎相同的結果爲標準的Java EXP。
  2. 實際algorith是更復雜的,與其它這樣的增量EXP(未雖然二次)
+0

代碼評論屬於http://codereview.stackexchange.com/您可能想要刪除#java標記。 –

+1

我不是要求代碼審查。我正在談論高效的數值方法。呼叫exp時間(慢)vs exp的乘積(快但不夠精確)。實際的代碼並不重要,我只是簡單地解釋我的問題 –

回答

1

這裏是我找到的最佳方案:

遞增誤差(種)直線與每個乘法由「單一的exp(a)」表示。對於某些err0,我們可以將該錯誤視爲類似於err(i) ~= i*i*err0的函數。重點在於v的誤差是二次方w.r.t i。

我發現的最好的是:

  1. 在一些選擇的頻率的V RESET爲正確的值(各k迭代)
  2. 提高u的正確性每個k次迭代,使用增量EXP計算

val k = 100 
val expA = Math.exp(-a) 
val expAk = Math.exp(-k*a) 
var u = 1.0 
var uk = 1.0 
var v = 1.0 

var i = 1 
while(i < n){ 
    if(i%k==0){ 
    uk *= expAk 
    u = uk 
    v = Math.exp(- 0.5*(i+1)*i * a) 
    } else{ 
    u *= expA 
    v *= u 
    } 
    i += 1 
} 

這種方法需要N/K + 2調用EXP,不太satifying但我現在是最好的。可以通過選擇最佳頻率參數k來改善。

相關問題