最近听了WerKeyTom_FTD的讲解,也是对矩阵乘法有了一个了解,在此总结一下。
简单认识:
设A=m*p,B=p*n,A*B=C(m*n),C[i,j]=∑A[i,k]*B[k,j](1<=k<=p)
斐波拉契数列:
设f(x)为斐波拉契数列中的第x项,且f(1)=0,f(2)=1,f(x)=f(x-1)+f(x-2),求f(n)。(1<=n<=10^9)。答案对10^9+7取模。
分析:
明显的是,如果直接去求,时间是O(n),接受不了10^9。
在初一上学期的时候,由于我还太年轻,不会矩阵乘法,于是我在草稿本上画啊画,终于发现了斐波拉契数列有一个神奇的规律:
我们来先看看一个斐波拉契数列:
0,1,1,2,3,5,8,13,21,34,55,89……
显然有
89
=34*1+55*1
=21*1+34*2
=13*2+21*3
=8*3+13*5
=…
我们竖着看第二列和第四列乘数,可以发现,它们又是一个斐波拉契数列,经过整理,可得:
f[n]=f[n-i]*f[i]+f[n-i+1]*f[i+1]
那么我们换一个角度想一下,
我们知道了
f[x],f[x+1]和f[y],f[y+1],f[y+2]
就可以知道
f[x+y]=f[x]*f[y]+f[x+1]*f[y+1],
也可以知道
f[x+y+1]=f[x]*f[y+1]+f[x+1]*f[y+2]
然后再用新得到的f[x+y],f[x+y+1]去推f[x+y+y],f[x+y+1+y]的值,以此类推,就能得到f[n]的值了。
f[y],f[y+1],f[y+2]看作常量,预先求出。
时间复杂度O(y+n/y)
还有最后一个问题:y=?,y+n/y最小。
其实当y=√n时,y+n/y最小。
一开始我也不会,于是去问了问数学老师Mrs.Chen,秒懂。马上证明:
我们知道有均值不等式:
a+b>=2√ab(a,b>=0)
那么
y+n/y>=2√y(n/y)
>= 2√n
很明显,只有当y=n/y,y=√n,(a=b)时,y+n/y(a+b)的值才会取最小值2√n(2√ab)
所以时间复杂度是O(√n)
让我们切入正题:
[f[x],f[x+1]]*[...]=[f[x+1],f[x+2]]
很容易推出转移矩阵(友矩阵)
[0,1]
[1,1]
因为矩阵乘法有结合性,所以可以提前对友矩阵进行乘法,使用矩阵快速幂,可以在O(log n)内解决。
代码:
type
arr=array[1..2,1..2] of int64;
const
mo=100000007;
var
n,i,j,k:longint;
a,b:arr;
procedure cheng(var a:arr;b:arr);
var c:arr;
begin
c:=a;
for i:=1 to 2 do
for j:=1 to 2 do
begin
a[i,j]:=0;
for k:=1 to 2 do
a[i,j]:=(a[i,j]+c[i,k]*b[k,j]) mod mo;
end;
end;
begin
readln(n);
if n=1 then
begin
writeln(0); halt;
end;
a[1,1]:=0; a[1,2]:=1; a[2,1]:=1; a[2,2]:=1;
b:=a;
n:=n-2;
while n>0 do
begin
if n and 1=1 then cheng(a,b);
n:=n>>1; cheng(b,b);
end;
writeln(a[1,2]);
end.