方法一:傳統遞歸法
時間復雜度O(2^n),空間復雜度O(n)
計算Fibonacci(10)十次平均用時0.0003s 計算Fibonacci(100)單次用時大於1min
時間復雜度極高,當n>35左右時間已經無法接受
def Fibonacci(n): if n == 1 or n == 2: return 1 return Fibonacci(n - 1) + Fibonacci(n - 2)
方法二:動態規划法
時間復雜度O(n),空間復雜度O(1)
計算Fibonacci(10)十次平均用時小於0.00001s 計算Fibonacci(100)十次平均0.0001s 計算Fibonacci(10000)十次平均0.008s 計算Fibonacci(1000000)十次平均9.525s
def Fibonacci(n): current, pre = 1, 0 for i in range(n - 1): current, pre, = current + pre, current return current
方法三:通項公式法
時間復雜度O(log n),空間復雜度O(1),通項公式法的時間復雜度不是O(1),這是因為計算n次冪不能做到O(1)時間復雜度,使用快速冪算法可以做到(log n)時間復雜度
計算Fibonacci(10)十次平均用時小於0.00001s 計算Fibonacci(100)十次平均用時小於0.00001s
計算Fibonacci(10000)數字太大,出現OverFlow錯誤 計算Fibonacci(1000000):數字太大,出現OverFlow錯誤
由於開方和四舍五入存在精度誤差,經過測試,使用通項公式法計算斐波那契數列在第71項時開始出現精度導致的計算錯誤。
根據測試可知,通項公式法只在理論上可行,沒有操作性。
def Fibonacci(n): sqrt5 = 5 ** 0.5 ans = (((1 + sqrt5) / 2) ** n - (((1 - sqrt5) / 2) ** n)) / sqrt5 return round(ans)
方法四:矩陣法
主要原理是以下公式和快速冪算法
這種算法是求任意線性常系數遞歸遞推關系的任意項的通用解法,而且通常也是最優解
時間復雜度O(log n),空間復雜度O(log n)(由遞歸深度決定)
計算Fibonacci(10)十次平均用時0.0002s 計算Fibonacci(100)十次平均0.0003s 計算Fibonacci(10000)十次平均0.0008s 計算Fibonacci(1000000)十次平均0.184s
可以看出當n較大時,矩陣法明顯優於動態規划法
另外,在這個方法中,使用了Strassen算法算法計算矩陣的乘法(該算法可以使得兩個大小為n的方朕相乘的時間復雜度由傳統的O(n ^ 3)下降到O(n ^ 2.81))
同時在快速冪算法中使用了一定的位運算技巧以達到最優化的性能
(筆者同時測試了把快速冪的遞歸算法換成非遞歸算法,非遞歸算法的時間大概是遞歸算法的兩倍)
def Matrix_Multiply(matrix1, matrix2): a, b, c, d = matrix1[0][0], matrix1[0][1], matrix1[1][0], matrix1[1][1] e, f, g, h = matrix2[0][0], matrix2[0][1], matrix2[1][0], matrix2[1][1] p1 = a * (f - h) p2 = (a + b) * h p3 = (c + d) * e p4 = d * (g - e) p5 = (a + d) * (e + h) p6 = (b - d) * (g + h) p7 = (a - c) * (e + f) r = p5 + p4 - p2 + p6 s = p1 + p2 t = p3 + p4 u = p5 + p1 - p3 - p7 return [[r, s], [t, u]] def Matrix_Quick_Pow(matrix, p): if p == 1: return matrix if p & 1: temp = Matrix_Quick_Pow(matrix, p >> 1) return Matrix_Multiply(Matrix_Multiply(temp, temp), matrix) else: tmp = Matrix_Quick_Pow(matrix, p >> 1) return Matrix_Multiply(tmp, tmp) def Fibonacci(n): matrix = [[1, 1], [1, 0]] return Matrix_Quick_Pow(matrix, n)[0][1]