初学矩阵乘法—斐波拉契数列

最近听了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.



你可能感兴趣的:(递推矩阵乘法优化)