矩阵快速幂

Posted jason66661010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了矩阵快速幂相关的知识,希望对你有一定的参考价值。

快速幂

点击这里

矩阵快速幂

最浅显的作用就是用来求一个矩阵的n次幂,就是将快速幂中的数字映射成矩阵

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 102
#define mod 1000000007//一般会对结果进行取余
int tmp[N][N],ans[N][N],ori[N][N];//ori是输入的矩阵,ans是承接结果的矩阵,tmp是临时矩阵
void mul(int a[][N],int b[][N], int n)
{
    //fill(tmp[0], tmp[0] + N*N, 0);
    memset(tmp, 0, sizeof tmp);
        for (int i = 0; i < n; i++)//矩阵的乘法
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                tmp[i][j] +=(a[i][k] * b[k][j]%mod)%mod;
    for (int i = 0; i < n; i++)//将tmp矩阵复制到a矩阵
        for (int j = 0; j < n; j++)
            a[i][j] = tmp[i][j];
}

void pow(int a[][N], int power)
{
    memset(ans, 0, sizeof ans);//n是幂,N是矩阵大小
    for (int i = 0; i < N; i++)
        ans[i][i] = 1;//设置为单位矩阵(与快速幂中的result是是一样的作用)
    while (power)//与单个数字的快速幂一样
    {
        if (power & 1)
            mul(ans, a, N);//是奇数就用ans承接结果
        power >>= 1;
        mul(a, a, N);//是偶数就执行a的平方来降幂
    }
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    int n,k;
    cin >> n >> k;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> ori[i][j];
    pow(ori, k);
    
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%s%d", j == 0 ? "" : " ", ans[i][j]);//输出结果
        cout << endl;
    }

}

实用代码

上面的代码只是为了较好地理解矩阵快速幂的算法实现,但是其中涉及到矩阵的复制、较多的初始化,在实际解题中会不适用,故如下给出实用代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 102
#define mod 1000000007
struct matrix//使用struct
{
    long long c[N][N];
}aa, bb;//注意:在这里初始化的aa、bb矩阵中默认值为0(aa表示初始矩阵,bb表示计算的结果矩阵)
long long ans[N][N], ori[N][N];
long long n, k;
matrix mul(matrix a, matrix b)
{
    matrix tmp;//这里初始化矩阵中的值为随机数,所以要将其设为0(也是为了后续的函数中能够使用tmp而不影响结果)
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            tmp.c[i][j] = 0;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            for (int k = 0; k < n; k++)
                tmp.c[i][j] = (tmp.c[i][j] + (a.c[i][k] * b.c[k][j]) % mod) % mod;
    return tmp;//直接返回struct结构体,不用矩阵复制
}

matrix pow(long long power)
{
    matrix ans;//这里初始化矩阵中的值为随机数,所以要将其设为0,对角线置为1,表示一个单位矩阵,否则后续结果会出错
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
        {
            if (i == j)ans.c[i][j] = 1;
            else ans.c[i][j] = 0;
        }

    while (power)//与单个数字的快速幂一样
    {
        if (power & 1)
            ans = mul(ans, aa);
        power >>= 1;
        aa = mul(aa, aa);
    }
    return ans;
}
int main()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            scanf("%lld", &aa.c[i][j]);
    bb = pow(k);

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%s%lld", j == 0 ? "" : " ", bb.c[i][j]);
        cout << endl;
    }

}

实用代码优化【模板】

上面代码的单位矩阵是后面新建的,而我们可以直接使用bb作为单位矩阵 给出矩阵快速幂的最后模板

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 102
#define mod 1000000007
struct matrix
{
    long long c[N][N];
}aa,bb;
long long ans[N][N], ori[N][N];
long long n, k;
matrix mul(matrix a, matrix b)
{
    matrix tmp;    
    for (int i = 0; i < n; i++)//这里初始化矩阵中的值为随机数,所以要将其设为0(也是为了后续的函数中能够使用tmp而不影响结果)
        for (int j = 0; j < n; j++)
            tmp.c[i][j] = 0;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            for (int k = 0; k < n; k++)
                tmp.c[i][j] =(tmp.c[i][j]+ (a.c[i][k] * b.c[k][j]) % mod) % mod;//注意这里的取余方式,现在乘法取余,防止在乘法就溢出
    return tmp;//直接返回struct结构体,不用矩阵复制
}

matrix pow(long long power)
{
    for (int i = 0; i < n; i++)
        bb.c[i][i] = 1;//使用bb作为单位矩阵来承接结果
    while (power)//与单个数字的快速幂一样
    {
        if (power & 1)
            bb=mul(bb, aa);
        power >>= 1;
        aa=mul(aa, aa);
    }
    return bb;
}
int main()
{
    scanf("%lld%lld", &n, &k);
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            scanf("%lld",&aa.c[i][j]);
    bb=pow( k);
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            printf("%s%lld", j == 0 ? "" : " ", bb.c[i][j]);
        cout << endl;
    }
}

简单练习

https://www.luogu.com.cn/problem/P3390(使用模板代码即可解决)

矩阵快速幂的用途

求解递推式的值(就像是高中数学的那种)

例题:https://www.luogu.com.cn/problem/P1939

解题步骤

第一步:列出递推式:ax=ax-1+ax-3(x>3)

第二步:ax最早是由ax-3三而来,而矩阵快速幂所解决的问题范围又是方阵,所以我们构造需要一个3*3的方阵

第三步:推理

这是第n项AN技术图片这是第n-1项AN-1技术图片于是我们构造的矩阵ans应该满足技术图片

 

将第n-1项横过来:f[n-1]  f[n-2]  f[n-3]  

f[n]= f[n-1]+f[n-3] :      1         0        1  (只需要f[n-1]    f[n-3]  )

 f[n-1]                   :      1         0        0  (第n-1项中有f[n-1]  )

 f[n-2]                   :      0         1        0  (第n-1项中有f[n-2]  )

 于是构造的ans矩阵就出来了

第四步:求解

这样一来就求出一个等比数列:An=ans*An-1——>An=ansn-1*A1(这里的A1就是【1 1 1 】即 初始a1 a2 a3的值)

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
#define N 102
#define mod 1000000007
struct matrix
{
    long long c[N][N];
}aa, bb;
long long ans[N][N], ori[N][N];
long long n, k;
matrix mul(matrix a, matrix b)
{
    matrix tmp;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            tmp.c[i][j] = 0;

    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            for (int k = 0; k < n; k++)
                tmp.c[i][j] = (tmp.c[i][j] + (a.c[i][k] * b.c[k][j]) % mod) % mod;
    return tmp;
}

matrix pow(long long power)
{
    for (int i = 0; i < n; i++)
        bb.c[i][i] = 1;
    while (power)//与单个数字的快速幂一样
    {
        if (power & 1)
            bb = mul(bb, aa);
        power >>= 1;
        aa = mul(aa, aa);
    }
    return bb;
}
int main()
{
    aa.c[0][0] = aa.c[0][2] = aa.c[1][0] = aa.c[2][1] = 1;//为初始的aa矩阵赋值(就是分析中的ans矩阵)
    n = 3;//矩阵的大小为3
    scanf("%lld",&k);
    for (int i = 0; i < k; i++)
    {
        long long tt;
        scanf("%lld", &tt);
        if (tt <= 3)
        {
            printf("1
"); continue;
        }
        bb = pow(tt-1);
        printf("%lld
", bb.c[0][0]);//在An项中,她是一个三行一列的矩阵,而所求的an就是第一行的值
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n; j++)
            {
                bb.c[i][j] = 0; aa.c[i][j] = 0;//因为要求多次,所以要重新初始化aa、bb矩阵
            }
        }
        aa.c[0][0] = aa.c[0][2] = aa.c[1][0] = aa.c[2][1] = 1;
    }

}

 

参考:https://blog.csdn.net/zhangxiaoduoduo/article/details/81807338https://blog.csdn.net/red_red_red/article/details/90208713、https://www.luogu.com.cn/blog/_post/101179

 

以上是关于矩阵快速幂的主要内容,如果未能解决你的问题,请参考以下文章

矩阵快速幂

初步 - 矩阵快速幂

poj 3233(矩阵快速幂)

模板之矩阵快速幂(luogu P3390模板矩阵快速幂)

数学问题——矩阵和矩阵快速幂

矩阵快速幂