關於遞迴問題的探討和優化,【線性遞迴】和【發散遞迴】(未完待續,更新中)
遞迴介紹
首先來說一下遞迴,我們不講概念,我只說一下遞迴本身,有需要的同學請自行查閱資料。
遞迴分兩個階段,遞和歸
- 遞:是用來描述函式在何種情況下需要呼叫自身遞下去
- 歸:是用來限定函式何時迴歸,因為程式不可能無限制的走下去
我們用數學表示式來看一下什麼是遞迴的表示
f(x,y) = x * f(x, y - 1)
f(x, 1) = x
上面的結構就是遞迴的數學表示啦,第一個函式式是定義了函式如何遞下去,第二個函式式定義了函式如何歸回來。
遞迴的缺點
上面的例子其實是求x的y次冪,按照遞迴的思想,我們計算表示式的結果需要把x連乘y次,乍看起來沒什麼,但如果當y的次數足夠大的時候,程式就可能會執行很慢,更甚至會發生爆棧的情況(也就是通常說的棧溢位)。
比如我們求2的21次冪的話,常規演算法就是把2連乘21次,相應程式碼如下:
/**
* @param x 底數
* @param y 指數
* @return x的y次冪
*/
long power(int x, int y) {
long res = 1;
for(int i = 1; i <= b; i++) {
res *= a;
}
return res;
}
是不是覺得有點low,那我們用遞迴的思想寫一個方法
/** * @param x 底數 * @param y 指數 * @return x的y次冪 */ long power(int x, int y) { if(y == 1) { return x; } else { return power(x, y-1) } }
上面兩個方法都不好,因為複雜度還是O(n)
所以為了解決這個問題,我們引出一個概念:整數快速冪
整數快速冪
所謂快速冪,就是快速求冪次。
利用我們學過二進位制,比如 21 ,它的二進位制數字為 10101 ,也就是他可以分成21 = 16 + 0 + 4 + 0 + 1,也就是21 = 1 * 2^4 + 1 * 2^2 + 1 * 2^0。
那麼我們計算2的21次冪就可以這麼算了 我們這樣分解2^21 = 2^16 * 2^4 * 2^1
如果要降低複雜度,首先應該是降低迴圈次數,再次就是降低重複計算的次數
先看程式碼
/** * @param x 底數 * @param y 指數 * @return x的y次冪 */ long power(int x, int y) { long res = 1; while(y) { if(y&1) { res = res * x; //如果二進位制最低位為1,則乘上x^(2^i) } x = x * x; //將x平方 y >>= 1; //右移 相當於n/2 } return res; }
這裡解釋一下,y&1是進行的與運算,相當於判斷y的二進位制表示最低為是不是1,>>這個是右移運算子,就是把當前數的二進位制表示向右移動一位,就是把10101變成1010.1,浮點後面的數字會自動丟棄
現在我們模擬一下程式是如何跑的 power(2, 21)
- res = 1
- while迴圈,此時y = 21 = 10101(二進位制)
- if(y&1),y = 10101(二進位制),判斷結果為true(二進位制的最低位是1)
- res = res * x = 1 * 2 = 2
- x = x * x = 2 * 2 = 2^2
- y >>= 1 = 1010(二進位制)
- while迴圈,此時y = 10101(二進位制)
- if(y&1),y = 1010(二進位制),判斷結果為false(二進位制的最低位是0)
- x = x * x = 2^2 * 2^2 = 2^4
- y >>= 1 = 101(二進位制)
- while迴圈,此時y = 101(二進位制)
- if(y&1),y = 101(二進位制),判斷結果為true(二進位制的最低位是1)
- res = res * x = 2 * 2^4 = 2^5
- x = x * x = 2^4 * 2^4 = 2^8
- y >>= 1 = 10(二進位制)
- while迴圈,此時y = 10(二進位制)
- if(y&1),y = 10(二進位制),判斷結果為false(二進位制的最低位是0)
- x = x * x = 2^8 * 2^8 = 2^16
- y >>= 1 = 1(二進位制)
- while迴圈,此時y = 1(二進位制)
- if(y&1),y = 1(二進位制),判斷結果為true(二進位制的最低位是1)
- res = res * x = 2^16 * 2^5 = 2^5
- x = x * x = 2^16 * 2^16 = 2^32
- y >>= 1 = 0(二進位制)
- while迴圈結束
- retrun res,也就是計算結果啦
線性遞迴
以上就是線性遞迴了,那什麼是線性遞迴?
線性遞迴的意思就是遞迴次數 和遞迴引數滿足線性表示式
也就是遞迴函式f(x) 需要遞迴的次數為 a*x+b 其中ab為常