Fibonacci數列高效解法大全及時間複雜度分析連載【8】

在數學上,斐波那契數列是以遞迴的方法來定義
……續上回 ofollow,noindex">Fibonacci數列高效解法大全及時間複雜度分析連載【7】
之前的篇章把各種Fibonacci數列的基本演算法討論過了
那麼是否可以做到更快呢,有什麼加速手段
這篇來說下
首先第一個手段是改進演算法的加速
16. 快速平方的矩陣解法
在 Fibonacci數列高效解法大全及時間複雜度分析連載【5】 這篇裡說過矩陣解法
矩陣法雖然跟二進位制模冪解法時間複雜度一樣,可算第100萬項斐波那契數用時是二進位制模冪解法的10倍。這是因為這演算法的時間常數項大
裡面用到了矩陣乘法,通用矩陣乘法演算法的時間複雜度是階數n的O(n^3)。也就是對一個二階矩陣,分解步驟中有8次乘法,非常耗時,造成矩陣解法時間常數項很大
之前程式是直接用的numpy庫做矩陣乘法,numpy庫裡就是通用矩陣乘法演算法
然而,對斐波那契數的矩陣解法,只需對二階矩陣做運算。而其中的二階矩陣平方步驟是有更小時間複雜度演算法的
二階矩陣快速平法演算法的時間複雜度為階數2的O(2^(log2(5)))
也就是二階矩陣一次平方運算分解步驟中只用做5次乘法。比通用矩陣演算法的8次降低了不少
但是,對斐波那契數矩陣解法而言,矩陣乘法步驟是其中的時間常數項,縮短矩陣乘法用時只是降低整個演算法的常數項,可並不降低整個演算法的時間複雜度
下面就是我寫的程式
import numpy import gmpy2 def fast_power_of_second_order_matrix(input_matrix: matrix, exponential: int) -> matrix: assert isinstance(exponential, int), 'Exponential is an error of non-integer type.' assert exponential >= 0, 'The exponent cannot be negative.' assert input_matrix.shape == (2, 2), 'It can only be a second-order matrix' if numpy.min_scalar_type(input_matrix) in (numpy.dtype(bool), numpy.dtype(int), numpy.dtype(float), numpy.dtype(complex)): output_matrix = numpy.asmatrix(numpy.identity(2, dtype = numpy.min_scalar_type(input_matrix))) #根據輸入矩陣的標量型別,把output_matrix初始化為相同型別的單位矩陣 elif numpy.issubsctype(input_matrix, numpy.dtype(object)): for matrix_element in input_matrix.flat: if type(matrix_element) not in (type(gmpy2.mpz()), type(gmpy2.xmpz()), type(gmpy2.mpq()), type(gmpy2.mpfr()), type(gmpy2.mpc()), int, float): raise TypeError('The matrix can only be of Boolean or numeric type.') output_matrix = numpy.mat(((gmpy2.mpz(1), gmpy2.mpz(0)), (gmpy2.mpz(0), gmpy2.mpz(1)))) #初始化值為mpz型別的單位矩陣 else: raise TypeError('The matrix can only be of Boolean or numeric type.') def square_of_second_order_matrix(input_second_order_matrix: matrix) -> matrix: output_second_order_matrix = numpy.copy(input_second_order_matrix) sum_of_main_diagonal = output_second_order_matrix[0, 0] + output_second_order_matrix[1, 1] product_of_antidiagonal = output_second_order_matrix[0, 1] * output_second_order_matrix[1, 0] output_second_order_matrix[0, 0] **= 2 output_second_order_matrix[1, 1] **= 2 output_second_order_matrix[0, 1] *= sum_of_main_diagonal output_second_order_matrix[1, 0] *= sum_of_main_diagonal output_second_order_matrix[0, 0] += product_of_antidiagonal output_second_order_matrix[1, 1] += product_of_antidiagonal return output_second_order_matrix intermediate_variable = numpy.copy(input_matrix) while True: if exponential & 1 == 1: output_matrix *= intermediate_variable if exponential == 1: break intermediate_variable = square_of_second_order_matrix(intermediate_variable) exponential >>= 1 return output_matrix
import numpy.matlib from gmpy2 import mpz def Fibonacci_sequence_06 (n: int) -> int: #引數n是表示求第n項Fibonacci數 '返回單項的矩陣解法' assert isinstance(n, int), 'n is an error of non-integer type.' Use_threshold_of_fast_power = 100000 if n>=2: fib_matrix = numpy.mat(((mpz(1), mpz(1)), (mpz(1), mpz(0)))) if n > Use_threshold_of_fast_power: fib_matrix = fast_power_of_second_order_matrix(fib_matrix, n - 1) else: fib_matrix **= n - 1 return fib_matrix[0,0] elif n == 1: return 1 elif n == 0: return 0 else: return None
Fibonacci_sequence_06(1000000)
用算到第100萬項Fibonacci數來測量下用時
Total time: 0.036093秒
比之前用通用矩陣乘法庫的程式縮短了15%的時間
注意,那麼一大段程式是有基礎開銷的
所以在非大數的時候用這個反而會不如通用庫快
怎麼辦呢
加一個閾值判別。當N大於閾值之後,才用這個二階矩陣快速平法演算法,數不大時就呼叫通用庫
搞定
歡迎點贊、留言
未完待續……