老問題,但人們可能仍然感興趣。
所以凱文用泰勒多項式得到了正確的想法,但是當你從它直接推導出你的算法時,你可能會陷入困境,主要是當你使用較大的截止值$我。
這是爲什麼: 在每一步,我的意思是每一個新的$ i,代碼調用bcfac($ i)。每次bcfac被稱爲執行$ i-1計算。而且,我一路高達299 ...這幾乎是45000操作!不是你的quick'n'easy浮點操作,但慢BC字符串操作 - 如果你設置bcscale(100)你的bcmul必須處理多達10000對字符!
此外,bcpow也隨着$ i的增加而減速。沒有bcfac那麼多,因爲它可以有效地使用類似於平方和乘法的方法,但它仍然增加了一些東西。
總體而言,所需時間隨着計算的多項式項的數量二次增長。
那麼......該怎麼辦?
這裏有一個技巧:
每當你處理多項式,尤其是泰勒多項式,使用霍納方法。
它轉換爲:exp(x)= x^0/0! + x^1/1! + x^2/2! + x^3/3! + ...
...成:EXP(X)=(((...)* X/3 + 1)* X/2 + 1)* X/1 + 1
突然之間,你根本不需要任何權力或因子。
function bc_exp($number) {
$result = 1;
for ($i=299; $i>0; $i--)
$result = bcadd(bcmul(bcdiv($result, $i), $number), 1);
return $result;
}
對於每一步,無論$ i是多少,這隻需要3個bc操作。 以$ i = 299的起始值(以與kevin代碼相同的精度計算exp),我們現在只需要897個bc操作,而不是45000. 即使使用30作爲截止值而不是300,我們現在只需要87個bc操作,而其他代碼仍然需要822個單獨的因子。
Horner's Method再次拯救了這一天!
一些其他的想法:
1)凱文的代碼將propably與輸入崩潰= 「0」,這取決於bcmath時如何處理錯誤,因爲代碼在第一步改掉bcpow(0,0)($ I = 0)。 2)較大的指數需要較長的多項式,因此需要更多的迭代,例如, bc_exp(300)會給出錯誤的答案,即使在$ i = 299的情況下,像bc_exp(3)這樣的東西也可以正常工作。 每學期增加x^n/n!到結果,所以這個項必須在多項式開始收斂之前變小。現在比較兩個連續的術語:
(x^(n+1)/(n+1)!)/(x^n/n!) = x/n
每個被加數比一個較大的前由x的因子/ N(我們通過霍納方法中使用),因此,爲了對於x ^(N + 1)/ (N + 1)!得到小的x/n也必須變小,這只是當n> x時的情況。
Inconclusio:只要迭代次數小於輸入值,結果就會發散。只有當您添加步驟直到迭代次數大於輸入次數時,算法纔會開始慢慢收斂。
爲了達到可以滿足願意使用bcmath的人的結果,您的$ i需要顯着大於您的$數量。當你試圖計算像e^346674567801
這樣的解決方案是將輸入分成整數部分和小數部分。 比整數部分使用bcpow和小數部分使用bc_exp,現在從開始收斂,因爲小數部分小於1.最後,乘以結果。
e^x = e^(intpart+fracpart) = e^intpart * e^fracpart = bcpow(e,intpart) * bc_exp(fracpart)
你甚至可以直接落實到上面的代碼:
function bc_exp2($number) {
$parts = explode (".", $number);
$fracpart = "0.".$parts[1];
$result = 1;
for ($i=299; $i>0; $i--)
$result = bcadd(bcmul(bcdiv($result, $i), $fracpart), 1);
$result = bcmul(bcpow(exp(1), $parts[0]), $result);
return $result;
}
注意,EXP(1)賦予您propably不會滿足您的需求爲bcmath時用戶一個浮點數。根據您的bcscale設置,您可能希望使用更準確的e值。3)談論迭代次數:在大多數情況下,300將會過度殺傷,而在其他一些情況下,它可能還不夠。一個算法,需要你的bcscale和$數字,並計算所需的迭代次數會很好。 Alraedy得到了一些涉及log(n!)的想法,但沒有具體的。
4)要使用此方法的任意基地,您可以使用^ x = e ^(x * ln(a))。 您可能希望在使用bc_exp(而不是在bc_exp2中執行此操作)之前將x分成它的intpart和fracpart,以避免不必要的函數調用。
function bc_pow2($base,$exponent) {
$parts = explode (".", $exponent);
if ($parts[1] == 0){
$result = bcpow($base,$parts[0]);
else $result = bcmul(bc_exp(bcmul(bc_ln($base), "0.".$parts[1]), bcpow($base,$parts[0]);
return result;
}
現在我們只需要編程bc_ln。我們可以使用與上面相同的策略:
取自然對數函數的泰勒多項式。 (因爲ln(0)沒有定義,所以取1作爲開發點) 使用Horner的方法來大幅度提高性能。 將結果轉換爲bc操作的循環。 當處理x> 1時,還要使用ln(x)= -ln(1/x)來保證收斂。
請注意,$ pow = 1/9223372036854775808 –