快速幂和矩阵快速幂(取模)算法

对于普通类型的求a^n,我们的求法是不是a*a*a*a....,这样乘以n次,时间复杂度为O(n),对于普通n比较小的我们可以接受,然而当n比较大的时候,计算就慢了,所以我们就去寻找更快捷的计算方法!

例如:我们要求2^8,我们通过当为偶数的时候,a^n=(a*a)^(n/2),当n为奇数时,a^n=a*(a*a)^(n/2)的形式,是不是可以转化为4^4->16^2->256^1,就可以了,2^5的话2*4^2->2*16^1。(把幂化为底数,减少乘法的次数)

int power(int a, int n)//a^n
{
    int ans = 1;
    while(n > 0) 
    {
        if(n&1) //当n为奇数时,乘以余下的一个a
            ans *= a;
        a *= a;
        n /= 2;
    }
    return ans;
}

但是当n比较大时往往我们的结果都非常大,如果直接算的话就会溢出,所以我们在计算的过程中会对结果取模,然后我们就有了快速幂取模算法。控制数据的大小,对于取模我们有(a*b)%c = ((a%c)*(b%c))%c,给出代码。 

int power(long long a, int n)
{
    long long ans = 1;
    while(n > 0) 
    {
        if(n&1) 
        {
            ans *= a;
            ans %= mod;
        }
        a *= a%mod;
        a %= mod;
        n /= 2;
    }
    return ans%mod;
}

下面是矩阵快速幂,区别只是底数换成了矩阵

const int N=10;
int n,mod;
int temp[N][N];
int res[N][N],a[N][N];
void mul(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]%mod)%mod;
    for(int i=0;i<N;i++)
        for(int j=0;j<N;j++)
            a[i][j]=temp[i][j];
    return ;
}
void fun(int nn)//快速幂,只不过底数换成了矩阵
{
    memset(res,0,sizeof(res));
    for(int i=0;i<N;i++)
        res[i][i]=1;//单位阵
    while(nn){
        if(nn&1)//奇数的话res×a
            mul(res,a);
        mul(a,a);//a自己平方
        nn>>=1;//幂次/2
    }
    return ;
}

应用如下:

我们从最简单的斐波那契数列入手,求菲波那切数列的第n项,n很大,所以MOD1e9+7

斐波的为前两项之和,即发f[0]=1,f[1]=1,f[i] = f[i-1]+f[i-2](i>1)

比较简单的可以直接得到递推式

 

,进一步递推

由于矩阵乘法满足结合律,所它也可以用快速幂算法求解。

然后给一些简单的递推式:
1.f(n)=a*f(n-1)+b*f(n-2)+c;(a,b,c是常数)

2.f(n)=c^n-f(n-1) ;(c是常数)

简单的例子

Fibonacci数列

考虑Fibonacci数列,  F(n)=F(n-1)+F(n-2)


将右边两项看做是一个列向量的形式,令 

 X_{n-1}=\left\{\begin{matrix}F_{n-1}\\F_{n-2}\end{matrix}\right\}


很容易得到Xn的形式,即 

X_{n}=\left\{\begin{matrix}F_{n}\\F_{n-1}\end{matrix}\right\}
现在的任务就是找到一个系数矩阵A,使得AX_{n-1}=X_n,且A需与n无关。 
如果能够找到这个n,则易知A^{n-1}X_1=X_n,于是可以利用矩阵快速幂计算出Xn。这样就可以在O(logn)的时间内计算出指定的Fibonacci数。 
这个矩阵很容易找,观察易得 

A=\left\{\begin{matrix}1&1\\1& 0\end{matrix}\right\}\tag{1}

Fibonacci数列变种

推广一下,如果令F_n=aF_{n-1}+bF_{n-2},则系数矩阵为 

A=\left\{\begin{matrix}a&b\\1& 0\end{matrix}\right\}\tag{2}

利用二项式展开构造矩阵

计算k次方和

Fibonacci数列只是最简单的例子,对于稍微复杂一点的例子,二项式展开是常用的一项技术。 
考虑计算: 

S_n=\sum_{i=1}^n{i^k}


易得 

Sn=n^k+Sn-1


如果仿照Fibonacci数列,令 

X_{n-1}=\left\{\begin{matrix}n^k\\S_{n-1}\end{matrix}\right\}


则 

X_{n}=\left\{\begin{matrix}(n+1)^k\\S_{n}\end{matrix}\right\}


此时,可求出 

A=\left\{\begin{matrix}\left({\frac{n+1}{n}}\right)^{k}&&0\\1&&1\end{matrix}\right\}


这个系数矩阵很明显是不能用的,因为它与n有关,无法将递推公式转化为矩阵的幂运算。 
这里需要使用二项式展开。 

S_n=(n-1+1)^k+S_{n-1}\\=C_k^0(n-1)^k+C_k^1(n-1)^{k-1}+\cdots+C_k^k+S_{n-1}


此时,如果令 

X_{n-1}=\left\{\begin{matrix}(n-1)^k\\(n-1)^{k-1}\\\vdots\\(n-1)^0\\S_{n-1}\end{matrix}\right\}


则有 

X_{n}=\left\{\begin{matrix}n^k\\n^{k-1}\\\vdots\\n^0\\S_{n}\end{matrix}\right\}


现在的任务与刚才一样,找到一个系数矩阵A,使得AXn-1=Xn,且A需与n无关。 
有关SnSn的递推公式实际上就指明了AA的最后一行。而利用二项式展开很容易得到其他行。 
有 

A=\left\{\begin{matrix}C_k^0&&C_k^1&&\cdots&&C_k^k&&0\\0&&C_{k-1}^0&&\cdots&&C_{k-1}^{k-1}&&0\\\vdots&&\vdots&&\ddots&&\vdots&&\vdots\\0&&\cdots&&\cdots&&C_0^0&&0\\C_k^0&&C_k^1&&\cdots&&C_k^k&&1\end{matrix}\right\}\tag{3}
最后有 

A^{n-1}X_1=X_n


且 

X_1=\left\{1\space1\space\cdots\space1\right\}^T


利用该系数矩阵可以在O(k3logn)O(k3log⁡n)时间内计算出kk次方的和。

更一般的例子

再考虑一个一般情况:

S_n=\sum_{i=1}^n{(ai+b)^k}


与刚才几乎一模一样,有 

S_n=[a(n-1)+(a+b)]^k+S_{n-1}\\=C_k^0a^k(n-1)^k+C_k^1a^{k-1}(a+b)(n-1)^{k-1}+\cdots+C_k^k(a+b)^k+S_{n-1}


剩下的步骤,也几乎完全与之前的一样,令 

X_{n-1}=\left\{\begin{matrix}(n-1)^k\\(n-1)^{k-1}\\\vdots\\(n-1)^0\\S_{n-1}\end{matrix}\right\}


则 

X_{n}=\left\{\begin{matrix}n^k\\n^{k-1}\\\vdots\\n^0\\S_{n}\end{matrix}\right\}


同样,有关SnSn的递推公式实际上就指明了AA的最后一行,其他行则利用二项式展开得到。 
有 

A=\left\{\begin{matrix}C_k^0&&C_k^1&&\cdots&&C_k^k&&0\\0&&C_{k-1}^0&&\cdots&&C_{k-1}^{k-1}&&0\\\vdots&&\vdots&&\ddots&&\vdots&&\vdots\\0&&\cdots&&\cdots&&C_0^0&&0\\C_k^0a^k&&C_k^1a^{k-1}(a+b)&&\cdots&&C_k^k(a+b)^k&&1\end{matrix}\right\}\tag{4}

更复杂的例子

假设要计算如下和式: 

S_n=\sum_{i=1}^ni^kk^i


同样,将其写成递推公式,有 

S_n=n^kk^n+S_{n-1}\\=(n-1+1)^kk^{(n-1)+1}+S_{n-1}\\=C_k^0(n-1)^kk^{(n-1)+1}+C_k^1(n-1)^{k-1}k^{(n-1)+1}+\cdots+C_k^kk^{(n-1)+1}+S_{n-1}


将与n有关的项抽出来作为列向量,令 

X_{n-1}=\left\{\begin{matrix}(n-1)^kk^{(n-1)+1}\\(n-1)^{k-1}k^{(n-1)+1}\\\vdots\\(n-1)^0k^{(n-1)+1}\\S_{n-1}\end{matrix}\right\}


则 

X_{n}=\left\{\begin{matrix}n^kk^{n+1}\\n^{k-1}k^{n+1}\\\vdots\\n^0k^{n+1}\\S_{n}\end{matrix}\right\}

同样,有关Sn的递推公式说明了系数矩阵AA的最后一行。而其他行仍然可以由二项式展开得到,只是相差了一个系数k而已,这很容易解决。 
最后,有 

A=\left\{\begin{matrix}kC_k^0&&kC_k^1&&\cdots&&kC_k^k&&0\\0&&kC_{k-1}^0&&\cdots&&kC_{k-1}^{k-1}&&0\\\vdots&&\vdots&&\ddots&&\vdots&&\vdots\\0&&\cdots&&\cdots&&kC_0^0&&0\\C_k^0&&C_k^1&&\cdots&&C_k^k&&1\end{matrix}\right\}\tag{5}

  • 36
    点赞
  • 167
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值