探索c++的函數pow()的實現方法·數學與程序設計的結合

 

計算t的m次冪的方法:(探索c++的函數pow()的實現方法,數學與程序設計的結合)

歡迎看看另一篇文章《實現log()和exp()函數的方法,並以此計算pow() 》增加了對Taylor展開後的進一步處理。
源程序下載:http://pjy.studio.googlepages.com/powP.cpp
或到我的收藏下載。   
    由於c++剛學完函數一章,而練習需要用pow()這個函數,於是就特發奇想,想自己能否寫一個能實現pow()功能的函數,經過一段努力,算有了一些結果。
    衆所周知,pow(double t,double m)是c++提供計算x的y次冪的函數,雖然系統提供了這個pow(),但我還是想自己寫一個自己的pow()。不過要寫出這個pow可能不太容易,因爲 指數m要求是double的,即可以是小數,那就不是簡單的循環可以做出的。
    剛開始想這個問題時,第一個想法是把y的整數部分和小數部分分離,例如若m=12.34,則把x^12.34轉化爲(x^12)*(x^34)/ (x^100),這樣就把指數爲double的情況轉化爲三個指數爲int的情況。而取其整數,最簡單的方法可能是int(y)了,而去小數則用m- int(m)即可,然後把小數按例子那樣分離。不過這裏可能存在兩個問題:
    1.int(y)可能會造成意想不到的結果,例如數據掉失。
    2.把一個浮點數分成三部份的工作比較麻煩。
    以上兩點,再加上我估計c++不大可能會用這種方法,而應該從數學方法去入手,所以我就另覓途徑了。說來也是比較巧合,我的數學分析課剛好學完了冪級數這一章,我就有了用把t^m展開成冪級數這一方向入手的想法,而剛好書上有這個例子: 
由冪級數的知識推出當-1<x<1,n->無窮(n爲整數)時恆有:(1+x)^m=1+mx+....[m*(m-1)*..*(m-n+1)*(x^n)]/(n!)+...
(具體證明不列出了)
並把通項記爲tmpm,即tmpm(n)=[m*(m-1)*..*(m-n+1)*(x^n)]/(n!)
    由於-1<x<1,那麼用這個方法只能算出0~2的m次冪,不過如果1+x>=2,那麼可以把它轉爲求1/(1+x)的-m次冪即可。 綜上述,也就可以求出正數t的m次冪了(把t^m看爲(1+x)^m)。估計c++的pow可能也是用類似的方法實現的。

    由公式,首先我們需要求x^n,即一個數的整數次冪,這個也不難:
double pow_i(double num,int n)//計算num的n次冪,其中n爲整數 
{
   double powint=1;
   int i;
   for(i=1;i<=n;i++) powint*=num;
   return powint;
}
    而後,我們還應該需要一個求n!的函數。
最後就可以寫出計算展開式的函數了:
double pow_f(double num,double m)//計算num的m次冪,num和m可爲雙精度,num大於零 
{
    int i,j;
    double powf=0,x,tmpm=1;
    x=num-1;
    for(i=1;tmpm>1e-12 || tmpm<-1e-12;i++)//當tmpm不在次範圍時,停止循環,範圍可改 
           {
           for(j=1,tmpm=1;j<=i;j++) 
                tmpm*=(m-j+1)*x/j;
                powf+=tmpm;
           }
    return powf+1;
}
注意到,我在實現求每一個n對應的tmpm時,並沒有用到pow_i(),也沒有用到一個函數來求n!。而是直接寫成tmpm*=(m-j+1)*x/j,並通過循環此式來求n對應的tmpm。這是有原因的:
    1.這樣寫更加簡潔。
    2.如果用函數返回n!時,會出現循環只會進行171次的後果,因爲171!已超過double的範圍。這樣有可能造成因爲循環不足而導致結果精確度差,而且有可能運行時出錯。
    
    到這裏,利用pow_f已能求出底數不大於2,指數爲實數的冪了。容易想到,對於底數t>=2的情況,可以把它轉爲求1/(1+x)的-m次冪即 可。理論上是如此,但實際上機卻顯示一個問題,就是當t比較小時(<1),而m比較大時,誤差十分大,這可能是因爲循環不足而造成的。也就是說,這 個pow_f不能求出t<1,m>1時比較精確的值,那樣的話對與t>2的情況也就求不出了,因爲作1/t的轉化後,又變成求底數小於 1的冪問題了。
    怎樣解決呢,我們說,是因爲t較小而m較大時,才出問題,這樣的話,不妨把m分開爲整數和小數(分別記爲mi,mf),各自對t求冪,再把兩個結果相乘不 就行了嗎,這樣對t^mi,可以用pow_i(),對t^mf,可以用pow_f,此時mf<1,用pow_f求t^mf,結果很準確。這個問題解 決了,那麼對於t>=2的問題也就解決了。
    最後,還有一個問題,就是t<0怎麼辦,在這裏,我的處理是,當t<0是,若指數m爲整數,則用pow_i求,而m不是整數,則返回0(c++的pow()好似是用錯誤處理的,不過我纔是個初學者,沒學到,所以就返回個0吧)。
    
    綜上述,我們還需要有一個函數來作爲入口,在調用pow_i和pow_f前處理這些情況:
double pow_ff(double num,double m)//調用pow_f()和pow_i(),計算num的m次冪,是計算冪的入口
{
    if(num==0 && m!=0) return 0;//若num爲0,則返回0
    else if(num==0 && m==0) return 1;// 若num和m都爲0,則返回1
    else if(num<0 && m-int(m)!=0) return 0;//若num爲負,且m不爲整數數,則出錯,返回0 
    if(num>2)//把底數大於2的情況轉爲(1/num)^-m計算
    {
        num=1/num;
        m=-m;
        }
    if(m<0) return 1/pow_ff(num,-m);//把指數小於0的情況轉爲1/num^-m計算
    if(m-int(m)==0) return pow_i(num,m);/*當指數爲浮點數是,分成整數和小數分別求
                                        冪,這是因爲但底數較小式,用pow_f直接求冪
                                        誤差大,所以分爲指數的整數部分用pow_i,小
                                        數部分用pow_f求.*/ 
    else return pow_f(num,m-int(m))*pow_i(num,int(m));
    return pow_f(num,m);
}

這樣,這個pow_ff基本上就和c++的pow差不多了,至於系統的pow究竟是怎麼實現的,是否也是這樣做的,就不太清楚了。而這此的練習,使我感到數學與編程結合的美妙,^_^。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章