用矩阵表示数列递推公式
中午随便翻翻《编程之美》,读了下求解Fibonacci数列这一章节。众所周知,对于Fibonacci数列,其递推关系式定义为:F(n) = F(n - 1) + F(n - 2), F(0) = 0, F(1) = 1。给定n,如何求出F(n)呢?
求解通项公式
学过组合数学的人应该会很自然的想到用特征方程的方法来解出通项公式,其结果为
不过当用计算机在求解一个比较大的n下的F(n)值时,由于通项公式中引入了无理数,所以不能保证结果的精度。当然还是分析下这种方法的时空复杂度吧,这种算法的空间复杂度为O(1),但是时间复杂度为O(log n)。书中认为时间复杂度是O(1),我认为是不对的,因为即使是调用库函数来计算幂次方,库函数在计算幂次方时,最快也只能达到O(log n)的复杂度。
矩阵方法解
由于Fabonacci数列是二阶递推数列,所以存在一个2*2的矩阵A,使得:
[Fn Fn-1] = [Fn-1 Fn-2] * A (1)
求解,其中A =
由(1)可以得到
[Fn Fn-1] = [Fn-1 Fn-1] * A = [Fn-2 Fn-3] * A2 = … = [F1 F0] * An-1
那如何求An呢?可以用分治策略来做,时间复杂度为O(log n),空间复杂度也是O(log n),具体的做法就参见《编程之美》中该专题的论述了。
用矩阵方法来表示数列递推公式,就不会碰到无理数,能够保证结果的精度;同时还能快速的得到针对某个M的余数(这个根据同余操作具有加法和乘法的性质可以论证)。
矩阵方法的发散
矩阵方法自然可以表示更多递推公式,并给出对应的矩阵相乘的计算公式。例如:F(n) = F(n - 1) + F(n - 2) + F(n - 3),其中初始值F(0)、F(1)、F(2)的值可以为任意的整数;F(n) = 3F(n - 1) + 2F(n - 2),其中初始值可以为任意的整数。(当然初始值为小数,不会在计算An时引入一丝的计算误差,而只是在最后一步的计算中影响结果的精度)
所以,形式化一点来讲,对于递推公式
F(n) = a1F(n - 1) + a2F(n - 2) + a3F(n - 3) + … + akF(n - k)
其中a1,a2,…,ak均为整数,初始值F(0), F(1), …, F(k - 1)可以为任意的初始值。对于这种形式的递推公式,都可以考虑用矩阵来表示递推公式。如下所示:
[Fn Fn-1 Fn-2 … Fn-k+1] = [Fn-1 Fn-2 Fn-3 … Fn-k] * A
= …
= [Fk-1 Fk-2 Fk-3 … F0] * An-k+1 (2)
其中A =
A为k*k方阵,同时第一列全为1,从第二列开始只有1个为1,其余为0。
我们回过头来看这个(2)式,
[Fn Fn-1 Fn-2 … Fn-k+1] = [Fk-1 Fk-2 Fk-3 … F0] * An-k+1
-------------------------- --------
第一部分 第二部分
简要分析就可以看出:第一部分即为由初始值构成的1*k矩阵,第二部分即为融合了递推公式信息的k*k方阵。
总结矩阵表示数列递推公式的好处:
-
用矩阵方法来表示数列递推公式,就避免应用通项公式却因为无理数而不能保证结果精度的麻烦。
-
同时还能快速地得到针对某个M的余数。
前辈将矩阵这个工具引入到代数,是非常棒的贡献!