1. 程式人生 > >關於遞迴問題的探討和優化,【線性遞迴】和【發散遞迴】(未完待續,更新中)

關於遞迴問題的探討和優化,【線性遞迴】和【發散遞迴】(未完待續,更新中)

遞迴介紹

首先來說一下遞迴,我們不講概念,我只說一下遞迴本身,有需要的同學請自行查閱資料。

遞迴分兩個階段,遞和歸

  • 遞:是用來描述函式在何種情況下需要呼叫自身遞下去
  • 歸:是用來限定函式何時迴歸,因為程式不可能無限制的走下去

我們用數學表示式來看一下什麼是遞迴的表示

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)

  1. res = 1
  2. while迴圈,此時y = 21 = 10101(二進位制)
  3. if(y&1),y = 10101(二進位制),判斷結果為true(二進位制的最低位是1)
  4. res = res * x = 1 * 2 = 2
  5. x = x * x = 2 * 2 = 2^2
  6. y >>= 1 = 1010(二進位制)
  7. while迴圈,此時y = 10101(二進位制)
  8. if(y&1),y = 1010(二進位制),判斷結果為false(二進位制的最低位是0)
  9. x = x * x = 2^2 * 2^2 = 2^4
  10. y >>= 1 = 101(二進位制)
  11. while迴圈,此時y = 101(二進位制)
  12. if(y&1),y = 101(二進位制),判斷結果為true(二進位制的最低位是1)
  13. res = res * x = 2 * 2^4 = 2^5
  14. x = x * x = 2^4 * 2^4 = 2^8
  15. y >>= 1 = 10(二進位制)
  16. while迴圈,此時y = 10(二進位制)
  17. if(y&1),y = 10(二進位制),判斷結果為false(二進位制的最低位是0)
  18. x = x * x = 2^8 * 2^8 = 2^16
  19. y >>= 1 = 1(二進位制)
  20. while迴圈,此時y = 1(二進位制)
  21. if(y&1),y = 1(二進位制),判斷結果為true(二進位制的最低位是1)
  22. res = res * x = 2^16 * 2^5 = 2^5
  23. x = x * x = 2^16 * 2^16 = 2^32
  24. y >>= 1 = 0(二進位制)
  25. while迴圈結束
  26. retrun res,也就是計算結果啦

線性遞迴

以上就是線性遞迴了,那什麼是線性遞迴?

線性遞迴的意思就是遞迴次數 和遞迴引數滿足線性表示式

也就是遞迴函式f(x) 需要遞迴的次數為 a*x+b 其中ab為常