假設有n+2個實數a0,a1,…,an,和x的序列,要對多項式Pn(x)= anxn +an-1xn-1+…+a1x+a0求值,直接方法是對每一項分別求值,並把每一項求的值累加起來,這種方法十分低效,它需要進行n+(n-1)+…+1=n(n+1)/2次乘法運算和n次加法運算。有沒有更高效的算法呢?答案是肯定的。通過如下變換我們可以得到一種快得多的算法,即Pn(x)= anxn +an-1xn-1+…+a1x+a0=((…(((anx+an-1)x+an-2)x+ an-3)…)x+a1)x+a0,這種求值的安排我們稱為霍納法則。
例如,當x=3時,計算p(x)=2x4-x3+3x2+x-5的值。對於多項式p(x)=2x4-x3+3x2+x-5,我們按霍納法則進行變換,有:
p(x)=2x4-x3+3x2+x-5
=x(2x3-x2+3x+1)-5
=x(x(2x2-x+3)+1)-5
=x(x(x(2x-1)+3)+1)-5
在實際的操作過程中,為了得到上式,我們沒有必要經過上述的特定變換,我們只需要一個該多項式系數的原始列表。我們可以方便地用一個二維表格來幫助我們筆算求出這個多項式的值。該表第一行包含了該多項式的系數(如果存在等於0的系數,也都包含進來),從最高的an到最低的a0。第二行中除了第一個和第二個單元格用來存儲x和an外,其它單元格都用來存儲中間結果。在作了這樣的初始化后,在計算第二行的某一個單元格的值時,用該單元格的前一個單元格乘以x的值再加上該單元格的第一行的系數即可。用這種方式算出的最后一個單元格的值,就是該多項式的值。
| 系數 |
a4 |
a3 |
a2 |
a1 |
a0 |
| 2 |
-1 |
3 |
1 |
-5 |
|
| x=3 |
2 |
3*2-1=5 |
3*5+3=18 |
3*18+1=55 |
3*55-5=160 |
所以,P(3)=160。我們拿表格中的單元格和多項式x(x(x(2x-1)+3)+1)-5做比較,我們會發現3*2-1=5是2x-1在x=3時的值,3*5+3=18是x(2x-1)+3在x=3時的值,3*18+1=55是x(x(2x-1)+3)+1在x=3時的值,最后3*55-5=160是x(x(x(2x-1)+3)+1)-5在x=3時的值。
二﹑霍納法則的程序實現
上述的計算過程我們可以用一個遞推的關系表示,即Pi(x)= xPi-1(x)+an-i,遞推的臨界值P0(x)= an,其中i=1…n。具體在實現時使用了滾動數組技術。
算法 Horner(p[0...n], x)
//用霍納法則求一個多項式在一個給定點的值
//輸入:一個n次多項式的系數數組p[0...n](從低到高存儲),以及一個數字x;
//輸出:多項式在x點的值
p = p[n];
for i = n - 1 downto 0 do
p = x * p + p[i];
return p;
滾動數組的作用在於優化空間,主要應用在遞推或動態規划中(如01背包問題)。因為DP題目是一個自底向上的擴展過程,我們常常需要用到的是連續的解,前面的解往往可以舍去。所以用滾動數組優化是很有效的。利用滾動數組的話在N很大的情況下可以達到壓縮存儲的作用。
滾動數組實際是一種節省空間的辦法,時間上沒啥優勢,多用於DP中。
三、霍納法則的副產品
該算法在計算p(x)在某些點x0上的值時所產生的中間數字,恰好可以作為p(x)除以x-x0的商的系數,而算法的最后結果,除了等於p(x0)外,還等於這個除法的余數。因此,對於我們的例子來說,p(x)=2x4-x3+3x2+x-5除以x-3的商和余數分別為2x3+5x2+18x+55和160.這種除法算法被稱為“綜合除法”,要比所謂的“長除法”更方便。
