演算法學習筆記(五) 遞迴之 快速冪、斐波那契矩陣加速
阿新 • • 發佈:2018-12-24
遞迴的定義
遞迴和迭代是程式設計中最為常用的基本技巧,而且遞迴常常比迭代更為簡潔和強大。它的定義就是:直接或間接呼叫自身。經典問題有:冪運算、階乘、組合數、斐波那契數列、漢諾塔等。其演算法思想:
- 原問題可分解子問題(必要條件);
- 原與分解後的子問題相似(遞迴方程);
- 分解次數有限(子問題有窮);
- 最終問題可直接解決(遞迴邊界);
對於遞迴的應用與優化,直接遞迴時要預估時空複雜度,以免出現用時過長或者棧溢位。優化遞迴就是以不做重複的事兒為原則進行。對於常見數列問題,常常會有遞推公式,也即 f(n) 和 f(n-1) 的關係式,由遞推公式其實就很容易寫出遞迴演算法的程式碼,這裡要重新詳細說一下遞迴和遞推的區別:
- 遞迴:將問題規模為n的問題,降解成若干個規模為n-1的問題,依次降解,直到問題規模可求,求出低階規模的解,代入高階問題中,直至求出規模為n的問題的解, ( n -> 0);
- 遞推:構造低階的規模(如規模為i,一般 i=0 ) 的問題,並求出解,推匯出問題規模為i+1的問題以及解,依次推到規模為n的問題, (0 -> n);
快速冪
直接轉換成程式碼,時間複雜度由樸素冪運算的 O(n) -> O(logn) :
/* a 的 n 次方的快速冪,C 程式碼 */ int quickpower(int a, int n) { if (n == 0) return 1; if (n % 2 == 1) return quickpower(a, n / 2) * quickpower(a, n / 2) * a; else return quickpower(a, n / 2) * quickpower(a, n / 2); }
斐波那契數列
轉換為程式碼,時間複雜度由直接遞迴的 O(n^2) -> O(logn) , 下面的實驗用 Python 實現,如果用 C++ 過載乘法運算子,則可以很大程度複用快速冪程式碼了就:
from time import clock from functools import reduce # 遞迴計算斐波那契數列 , python3 實現 def fib1(n): if n <= 1 : return 1 else : return fib1(n - 1) + fib1(n - 2) # 計算 a, b (都是 2×2 的矩陣) 的乘積 def mul(a, b): r = [[0, 0], [0, 0]] r[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0]; r[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1]; r[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0]; r[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1]; return r; # 遞迴加速計算斐波那契數列 O(n^2) -> O(logn) def fib(n): if n == 0: return [[1, 0], [0, 1]] if n == 1: return [[1, 1], [1, 0]] if n % 2 == 0 : return mul(fib(int(n / 2)), fib(int(n / 2))) else: return reduce(mul,[fib(int(n / 2)),fib(int(n / 2)),[[1, 1], [1, 0]]]) if __name__ == '__main__': starttime = clock() print(fib1(35)) endtime = clock() print('直接計算用遞迴:', endtime - starttime) starttime = clock() print(fib(35)[0][0]) endtime = clock() print('矩陣遞迴冪加速:', endtime - starttime) ''' 試驗執行結果: 14930352 直接計算用遞迴: 5.524596036360783 14930352 矩陣遞迴冪加速: 0.00015129910309763517 '''
【原文地址:http://blog.csdn.net/thisinnocence】