首页 > 其他分享 >矩阵快速幂

矩阵快速幂

时间:2023-12-10 12:36:25浏览次数:21  
标签:int ll 矩阵 times ij 快速 105

前言

关于这个算法的前置知识快速幂矩阵可以点击链接看我以前的博客

问题

给定\(n \times n\)矩阵\(A\),求\(A^k\)

算法思路

顾名思义,矩阵快速幂就是矩阵乘法 + 快速幂
(这里就不再赘述快速幂的原理,不熟悉的可以去看我以前的博客)
要想实现这个算法,我们首先需要先实现矩阵乘法,设:

\[A = (a_{ij})_{n \times m}, B = (b_{ij})_{m \times p} \]

\[AB = C = (c_{ij})_{n \times p} \]

对于

\[i = 1, 2, ..., n, j = 1, 2, ..., p \]

\[c_{ij} = \sum_{k = 1}^{m}a_{ik}b_{kj} \]

相比于上述描述,矩阵乘法用代码写出来反而更加通俗易懂:

int a[n][m], b[m][p];
int c[n][p] = {0};

for(int k = 1; i <= m; i++)
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= p; j++)
            c[i][j] += a[i][k] * b[k][j];

对于快速矩阵幂中遇到的\(A = (a_{ij})_{n \times n}\)的情况则更加简单:\(k, i, j\)均小于等于\(n\)。也就是说:

\[AA = C = (c_{ij})_{n \times n} \]

对于

\[i = 1, 2, ..., n, j = 1, 2, ..., n \]

\[c_{ij} = \sum_{k = 1}^{n}a_{ik}b_{kj} \]

现在考虑将矩阵乘法运用到快速幂中,那么我们就要实现两个乘法操作:
\(A\)自乘以及将每部分幂的结果乘起来。
前者用\(A \times A\)即可:

int a[n][n];
int c[n][n] = {0};

for(int k = 1; i <= n; i++)
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            c[i][j] += a[i][k] * a[k][j];

后者我们可以使用单位矩阵\(I \times A\),所以我们还需要定义单位矩阵。
由单位矩阵的定义我们可以写出代码:

int I[n][n] = {0};
for(int i = 1; i <= n; i++) I[i][i] = 1;

然后实现\(I \times A\):

int a[n][n];
int I[n][n] = {0};
int c[n][n] = {0};

for(int i = 1; i <= n; i++) I[i][i] = 1;

for(int k = 1; i <= n; i++)
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            c[i][j] += ans[i][k] * a[k][j];

将上述操作添加到快速幂中:在\(n\)(\(n\)指代要求的次数)不等于\(0\)时每次让\(A\)自乘,并且当\(n\)的最后一位二进制是\(1\)时将\(A \times I\);上述操作结束后去掉\(n\)的最后一位二进制位。

代码实现

这里以洛谷P3390 【模板】矩阵快速幂为例

#include <iostream>

#define ll long long

using namespace std;

const int N = 1e9 + 7;

ll a[105][105];
ll ans[105][105] = {0};    //将ans[][]作为保存结果的矩阵
ll n, b;   //b即为次数

void work1() {      //A自乘
    ll c[105][105] = {0};  //用来保存这次运算的结果
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                c[i][j] = (c[i][j] + a[i][k] * a[k][j]) % N;
    for(int i = 1; i <= n; i++) 
                for(int j = 1; j <= n; j++) 
                        a[i][j] = c[i][j];
}

void work2() {      //保存结果
    ll c[105][105] = {0};  //用来保存这次运算的结果
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
                c[i][j] = (c[i][j] + ans[i][k] * a[k][j]) % N;
    for(int i = 1; i <= n; i++) 
        for(int j = 1; j <= n; j++) 
            ans[i][j] = c[i][j];
}

void matrixQuickPow() {
    while(b != 0) {
        if((b & 1) == 1)
            work2();
        work1();
        b >>= 1;
    }
}

int main(void) {
    cin >> n >> b;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            cin >> a[i][j];
        }
    }
    for(int i = 1; i <= n; i++) ans[i][i] = 1;  //将ans初始化为单位矩阵
    
    matrixQuickPow();

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            cout << ans[i][j] % N << " ";
        }
        cout << endl;
    }

    return 0;
}

标签:int,ll,矩阵,times,ij,快速,105
From: https://www.cnblogs.com/dbywsc/p/17892380.html

相关文章

  • 3分钟快速上手springBoot全局异常处理
    统一异常处理前后端都是有个统一的格式返回如Result,中有code,message,data。而若service、controller抛出异常则会导致不是统一格式的返回而是以下格式:而导致前端接受不到约定好的code,message最终导致内部发生异常而用户却得不到最基本的反馈。可以通过java中统一异常处理的......
  • 使用FreeFileSync快速实现本地数据备份与FTP远程数据迁移
    数据是电脑中最重要的东西。为了保证数据安全,我们经常会对数据进行备份。之前一直采用将重要数据拷贝至移动硬盘的方式实现备份,实现简单但每次都需要把所有文件拷贝一次,当文件很大时效率较低。因此,考虑使用FreeFileSync软件实现数据备份。该软件使用C++语言编写、免费、开源......
  • git快速使用
    1.初始化仓库#创建时默认初始化一个分支为mastergitinit#创建时初始化一个分支#gitinit-b<branch-name>gitinit-bmain2.配置用户名和邮箱#不加--global默认配置成当前仓库#--global配置全局,每次的git提交都会用此信息gitconfig.user="用户名"git......
  • Java开发者的Python快速进修指南:实战之跳表pro版本
    之前我们讲解了简易版的跳表,我希望你能亲自动手实现一个更完善的跳表,同时也可以尝试实现其他数据结构,例如动态数组或哈希表等。通过实践,我们能够发现自己在哪些方面还有所欠缺。这些方法只有在熟练掌握之后才会真正理解,就像我在编写代码的过程中,难免会忘记一些方法或如何声明属性等......
  • 第四讲 数学知识——快速幂
    AcWing875.快速幂\(O(n\log_2b)\)#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;typedeflonglongll;intn,a,b,p;intqp(inta,intb,intp){intres=1;while(b){if(b&......
  • 详解十大经典排序算法(六):快速排序(QuickSort)
    算法原理分区(Partition):选择一个基准元素,将数组分为两个子数组,小于基准的放在左边,大于基2准的放在右边。递归排序:对左右两个子数组分别进行快速排序。合并:不需要实际的合并操作,因为在分解和递归排序阶段已经完成了排序。算法描述快速排序是一种基于分治思想的高效排序算法,由英国......
  • 如何利用OPeNDAP快速读取格点数据——以GFS为例
    国内的气象圈子对于OPeNDAP这个单词应该是既熟悉又陌生,熟悉就熟悉在它出现频率很高,感觉好像哪哪儿都提到了它;而陌生就陌生在平时实际工作中好像又很少真正用过它。事实上OPeNDAP是一个可以极大提高格点数据传输和使用效率的“工具”,当初我第一次体验这个东西的时候就发出了“......
  • 简单封装PhpSpreadsheet,实现PHP快速导入、导出xlsx
    简单封装PhpSpreadsheet,实现PHP快速导入、导出xlsx<?phpnamespacexfstu\tools;usePhpOffice\PhpSpreadsheet\Spreadsheet;usePhpOffice\PhpSpreadsheet\Writer\Xlsx;usePhpOffice\PhpSpreadsheet\IOFactory;/***@methodexport(array$field,array$data)简单封......
  • Python算法——快速排序
    快速排序(QuickSort)是一种高效的分治排序算法,它选择一个基准元素,将数组分成两个子数组,小于基准的放在左边,大于基准的放在右边,然后递归地排序子数组。快速排序通常比冒泡排序和选择排序更高效,特别适用于大型数据集。本文将详细介绍快速排序的工作原理和Python实现。快速排序的工作原......
  • 用AntDesignBlazor快速开发一个权限系统
    写在前面:如果您是一个C#的后台开发人员,又或是C#的WPF开发人,如果想快速开发自己的网站系统,那么选择Blazor技术是太适合你不过了。(在没有Blazor之前,我会推荐Vue),尤其当我看到AntDesginBlazor(https://antblazor.com/zh-CN/)全家桶的时候,毫不犹豫就开始了我的愉快之旅。一、登录界面......