矩阵快速幂

假装没事ソ 提交于 2020-01-24 02:19:02

1.快速幂

快速幂也叫二进制取幂,想要快速求出 ana^n的结果就用它来求。

ana^n表示将n个a乘在一起,时间复杂度是O(n) 级别,如果n太大的话这种方法就不适用了。

如果使用快速幂,时间复杂度能达到O(log2log_2n),因为n有⌊log2log_2n⌋ + 1 个二进制位,所以当知道了每一位的值后,只需要通过log2log_2n次乘法就可计算出ana^n

用快速幂进行计算
例如计算a9a^9,9的二进制是1001, 9 = 1 × 202^0 + 0 × 212^1 + 0 × 222^2 +1 × 232^3 = 212^1 + 232^3 ;
因此 a9a^9 = a(2^1 + 2^3)= a2^1 * a2^3 , 也就是a1 * a8。现在这样就快多了吧,原来9个a连乘要计算8次乘法,现在使用快速幂只要乘两次。
但是像a8这种式子好像不是很好求的样子。。。

long long pow(int a, int b)
{
    long long int ans = 1;
    while(b)
    {
        if(b & 1) //判断b的二进制最后一位是不是1
        {
        	 ans *= a;
        }
        a *= a;  //累乘,以便对ans做出贡献
        b >>= 1; //b右移一位,去掉最后一位
    }
    return ans;
}

关于代码中累乘 a *= a
a *= a 即 a = a2,再下一步就是a2 * a2 = a4,再下一步就是a4 * a4 = a8… …
这样随着b右移,a也发生变化:a1 -> a2->a4->a8… … a的指数恰好是2x形式,再看a9 = a1 * a8,类似于 a8这样的式子就完美求出来了,当b二进制当前的末位为1的时候,只要把a乘给ans就能更新ans了。

2.矩阵快速幂

两个矩阵相乘
(a11a12...a1na21a22...a2na31a32...a3n)(b11b12...b1nb21b22...b2nb31b32...b3n)=(c11c12...c1nc21c22...c2nc31c32...c3n) \begin{pmatrix} a_{11} & a_{12}& ... & a_{1n} \\ a_{21} & a_{22} & ... & a_{2n} \\ a_{31} & a_{32} & ... & a_{3n} \end{pmatrix} *\begin{pmatrix} b_{11} & b_{12} & ... & b_{1n} \\ b_{21} & b_{22} & ... & b_{2n} \\ b_{31} & b_{32} & ... & b_{3n} \end{pmatrix}=\begin{pmatrix} c_{11} & c_{12}& ... & c_{1n} \\ c_{21} & c_{22} & ... & c_{2n} \\ c_{31} & c_{32} & ... & c_{3n} \end{pmatrix}
举个例子
(abcd)(a1b1c1d1)=(aa1+bc1ab1+bd1ca1+dc1cb1+dd1) \begin{pmatrix} a & b \\ c & d \end{pmatrix} * \begin{pmatrix} a_1 & b_1 \\ c_1 & d_1 \end{pmatrix}=\begin{pmatrix} a*a_1 +b*c_1 & a*b_1+b*d_1 \\ c*a_1+d*c_1 & c*b_1+d*d_1 \end{pmatrix}

注意:
1.矩阵A*B必须满足 A的列数=B的行数

2.若矩阵A是一个m行s列的矩阵,矩阵B是一个s行n列的矩阵,则C=A*B是一个m行n列的矩阵

矩阵快速幂
设A为矩阵,求An,若直接相乘,需要连乘n-1次,时间复杂度为O(n),
使用矩阵快速幂,时间复杂度为O(log2n)log_2n)

那么如何实现矩阵快速幂呢?只要快速幂中的乘法改成矩阵的乘法就行了。

const int N = 2;
int ans[N][N];
int temp[N][N];
int num[N][N];
void multi(int a[][N], int b[][N]) //两个矩阵相乘
{
    memset(temp, 0, sizeof(temp));
    for(int i = 0; i < N; i++)
        for(int j = 0; j < N; j++)
        for(int k = 0; k < N; k++)
        temp[i][j] = temp[i][j] + a[i][k] * b[k][j];

    for(int i = 0; i < N; i++)
        for(int j = 0; j < N; j++)
        a[i][j] = temp[i][j];
}
void MatPow(int a[][N], int n)  //计算矩阵a的n次方
{
    memset(res, 0, sizeof(res));
    for(int i = 0; i < N; i++) ans[i][i] = 1; //初始化为单位矩阵,相当于快速幂中的ans = 1

    while(n)
    {
        if(n&1)
        {
            multi(ans, a); //矩阵ans与矩阵a相乘
        }
        multi(a, a);//矩阵a自乘
        n >>= 1;
    }
}

矩阵快速幂主要是用来求一个复杂递推式的某一项问题,只要构造出了关系矩阵,问题就好解决了。

如何构造矩阵

首先说明一下,能用矩阵快速幂优化的递推类型是f[n]=2f[n-1]+3f[n-2]+n3+1这类的,也就是递推要是线性的,且f[n-i]的系数要是常数,可以含有n的多项式。

下面介绍一些递推式:
递推式1.斐波那契数列Fn=Fn1+Fn2,F0=1,F1=2,FnF_n=F_{n-1}+F_{n-2},F_0=1, F_1=2,求F_n
设转换矩阵为A,那么
A(Fn2Fn1)=(Fn1Fn)=(Fn1Fn1+Fn2) A* \begin{pmatrix} F_{n-2} \\ F_{n-1} \end{pmatrix} = \begin{pmatrix} F_{n-1} \\ F_{n} \end{pmatrix} =\begin{pmatrix} F_{n-1} \\ F_{n-1} +F_{n-2} \end{pmatrix}

之前说过,若矩阵a是一个m行s列的矩阵,矩阵b是一个s行n列的矩阵,则c=a*b是一个m行n列的矩阵


易知,A是一个2×2的矩阵。

A=(abcd) A= \begin{pmatrix} a & b \\ c & d \end{pmatrix}
那么
aFn2+bFn1=Fn1a * F_{n-2}+b*F_{n-1} = F_{n-1},
cFn2+dFn1=Fn1+Fn2c * F_{n-2}+d*F_{n-1} = F_{n-1}+F_{n-2}
得a=0,b=1,c=1,d=1
A=(0111) A= \begin{pmatrix} 0 & 1 \\ 1 & 1 \end{pmatrix}
这样就求出了转换矩阵A

A(F1F2)=(F2F3) A* \begin{pmatrix} F_1 \\ F_2 \end{pmatrix} = \begin{pmatrix} F_2 \\ F_3 \end{pmatrix}

A(F2F3)=(F3F4) A* \begin{pmatrix} F_2 \\ F_3 \end{pmatrix} = \begin{pmatrix} F_3 \\ F_4 \end{pmatrix}
An1(F1F2)=(FnFn+1) A^{n-1}* \begin{pmatrix} F_1 \\ F_2 \end{pmatrix} = \begin{pmatrix} F_n \\ F_{n+1} \end{pmatrix}

最后得到的矩阵的第一个元素即为FnF_n
一些类似的数列:

(1)Fn=Fn1+Fn2+1,F1=1,F2=2,FnF_n = F_{n-1}+F_{n-2} + 1,F_1=1, F_2=2,求F_n

A(Fn2Fn11)=(Fn1Fn1)=(Fn1Fn2+Fn1+11) A* \begin{pmatrix} F_{n-2} \\ F_{n-1} \\ 1 \end{pmatrix} = \begin{pmatrix} F_{n-1} \\ F_n\\ 1 \end{pmatrix}= \begin{pmatrix} F_{n-1} \\ F_{n-2}+F_{n-1} + 1 \\ 1 \end{pmatrix}
解得 A=(010110011) A= \begin{pmatrix} 0 & 1 & 0\\ 1 & 1 & 0 \\ 0 & 1 & 1 \end{pmatrix}
FnF_n:
An1(F1F21)=(FnFn+11) A^{n-1}* \begin{pmatrix} F_1 \\ F_2 \\ 1 \end{pmatrix} = \begin{pmatrix} F_n \\ F_{n+1} \\ 1 \end{pmatrix}
最后得到的矩阵的第一个元素即为FnF_n

(2)Fn=Fn1+Fn2+n+1,F0=1,F1=2,FnF_n = F_{n-1}+F_{n-2} + n + 1,F_0=1, F_1=2,求F_n

A(Fn2Fn1n1)=(Fn+1Fnn+11)=(Fn+1Fn2+Fn1+n+1n+11) A* \begin{pmatrix} F_{n-2} \\ F_{n-1} \\ n \\ 1 \end{pmatrix} = \begin{pmatrix} F_{n+1} \\ F_n \\ n+1 \\ 1 \end{pmatrix} = \begin{pmatrix} F_{n+1} \\ F_{n-2}+F_{n-1}+n+1 \\ n+1 \\ 1 \end{pmatrix} 解得 A=(0100110001100111) A= \begin{pmatrix} 0 & 1 & 0 & 0 \\ 1 & 1 & 0 & 0\\ 0 & 1 & 1 & 0 \\ 0 & 1 & 1 & 1 \end{pmatrix}
FnF_n: An1(F1F231)=(FnFn+1n+11) A^{n-1}* \begin{pmatrix} F_1 \\ F_2\\ 3 \\ 1 \end{pmatrix} = \begin{pmatrix} F_n \\ F_{n+1} \\ n+1 \\ 1 \end{pmatrix}

(3)Fn=Fn2+Fn1,F1=F2=1,FnnSnF_n=F_{n-2}+F_{n-1},F_1=F_2=1,求F_n的前n项和S_n

同理求A:
A(Fn2Fn1Sn2)=(Fn1FnSn1)=(Fn1Fn2+Fn1Sn2+Fn1) A* \begin{pmatrix} F_{n-2} \\ F_{n-1} \\ S_{n-2} \end{pmatrix} = \begin{pmatrix} F_{n-1} \\ F_n\\ S_{n-1} \end{pmatrix}= \begin{pmatrix} F_{n-1} \\ F_{n-2}+F_{n-1} \\ S_{n-2}+F_{n-1} \end{pmatrix}
解得 A=(0100011100001000101001011) A= \begin{pmatrix} 0 & 1 & 0 & 0 & 0\\ 1 & 1 & 1 & 0 & 0\\ 0 & 0 & 1 & 0 & 0\\ 0 & 1 & 0 & 1 & 0\\ 0 & 1 & 0 & 1 & 1 \end{pmatrix}

观察上面数各列求出的转换矩阵A,你会发现它们有一些相似性,
一般的,如果有Fn=pFn2+qFn1+rn+sF_n=p*F_{n-2} + q * F_{n-1} + r * n + s,可构造矩阵A为:
A=(0p0001q100001000r0100s011) A= \begin{pmatrix} 0 & p & 0 & 0 & 0\\ 1 & q & 1 & 0 & 0\\ 0 & 0 & 1 & 0 & 0\\ 0 & r & 0 & 1 & 0\\ 0 & s & 0 & 1 & 1 \end{pmatrix}
递推式2:Fn=kFn1+n2Fn=k*F_{n-1}+n^2
按照之前的方法写:
A(Fn1n2)=(Fn(n+1)2)=(kFn1+n2(n+1)2) A* \begin{pmatrix} F_{n-1} \\ n^2 \end{pmatrix} = \begin{pmatrix} F_n \\ (n+1)^2 \end{pmatrix}= \begin{pmatrix} k*F_{n-1}+n^2 \\ (n+1)^2 \end{pmatrix} 易知A是2×2的矩阵,不访设 A=(abcd) A= \begin{pmatrix} a & b \\ c & d \end{pmatrix}
得到:
aFn1+bn2=kFn1+n2a*F_{n-1} + b * n^2 = k* F_{n-1} + n^2 (式子1)
cFn1+dn2=(n+1)2=n2+2n+1c*F_{n-1}+d*n^2=(n+1)^2=n^2+2n+1(式子2)
解到这里你会发现式子2中有一个关于n的一次项2n和一个常数项,此时求出的abcd的值是不对的,因为将abcd带入原式后计算,是得不到(n+1)2(n+1)^2这个式子的,所以不能这样构造。那应该怎么构造呢,在式子2中有一个一次项和一个常数项,那就再构造时把一次项和常数项加入。
A(Fn1n2n1)=(kFn1+n2(n+1)2n+11) A* \begin{pmatrix} F_{n-1} \\ n^2 \\ n \\ 1 \end{pmatrix} = \begin{pmatrix} k* F_{n-1}+n^2 \\ (n+1)^2 \\ n+1 \\ 1 \end{pmatrix}

A=(k100012100100001) A= \begin{pmatrix} k & 1 & 0 & 0 \\ 0 & 1 & 2 & 1\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}
递推式3: Fn=kFn1+n3F_n=k*F_{n-1}+n^3
同理可求得:
A=(k100001331001210001100001) A= \begin{pmatrix} k & 1 & 0 & 0 & 0\\ 0 & 1 & 3 & 3 & 1\\ 0 & 0 & 1 & 2 & 1 \\ 0 & 0 & 0 & 1 & 1 \\ 0 & 0 & 0 & 0 & 1 \end{pmatrix}
递推式4:Fn=kFn1+bnF_n=k*F_{n-1}+b^n
A(Fn1bn)=(Fnbn+1)=(kFn1+bnbnb) A* \begin{pmatrix} F_{n-1} \\ b^n \end{pmatrix}= \begin{pmatrix} F_n \\ b^{n+1} \end{pmatrix}= \begin{pmatrix} k*F_{n-1}+b^n \\ b^n * b \end{pmatrix}
解得:
A=(k10b) A= \begin{pmatrix} k & 1 \\ 0 & b \end{pmatrix}

几道模板题:
1.Fibonacci
2.Tr A
3.A Simple Math Problem

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!