整数快速幂, 矩阵加速, 矩阵快速幂超详细讲解

一, 整数快速幂

1. 简介

该算法就是让计算机更快的求出 a b a^b ab 的值的一个算法, 如果采用暴力算法, 那么计算机需要计算 b b b 次, 如果 b b b 很大的话, 那么时间复杂度是很大的, 但如果采用 快速幂算法, 时间复杂度就是 O ( l o g b ) O(logb) O(logb) 级别的, 怎么样, 是不是很心动呢?

2. 原理

这种算法之所以这么快, 是因为它符合计算机的思维, 它利用了二进制.

举个例子: 十进制的数11 可以换算成二进制的数 ( 1011 ) 2 (1011)_2 (1011)2, 从左到右, 这些1分别代表十进制中的 $ 8 $, $ 2 $, 1 1 1, 所以 a 11 a^{11} a11 = = = $ a^{8+2+1} $ = = = a 8 ∗ a 2 ∗ a 1 a^8*a^2*a^1 a8a2a1.

好的, 有了上面的例子帮助理解, 下面我们直接叙述快速幂算法的原理:

  1. 如果将 a a a 自乘一次, 那么会得到 a 2 a^2 a2, 再自乘一次, 会得到 a 4 a^4 a4, … \dots , 自乘 n n n 次, 我们会得到 a 2 n a^{2n} a2n.
  2. a x + y a^{x+y} ax+y = = = a x ∗ a y a^x*a^y axay
  3. 一个十进制数 b b b 有一个唯一的二进制数与之对应

这三条原理的正确性应该不需要证明吧!

3. 算法流程

对于一个幂 a b a^b ab.
  1. b b b 写成二进制数的形式
  2. 定义一个基底 b a s e base base = = = a a a, 定义一个储存结果的变量 a n s ans ans = = = 1 1 1.
  3. 从右至左遍历 b b b 的二进制位, 如果该位是 1 1 1, 则 a n s ans ans = a n s ans ans ∗ * b a s e base base, 若该位为 0 0 0, 则不对 a n s ans ans 进行处理. 无论该位为什么, 始终有 b a s e base base = b a s e 2 base^2 base2.
  4. 遍历结束, 输出 a n s ans ans
举一个实际的例子吧, 以 2 13 2^{13} 213 举例吧
  1. b b b = ( 1101 ) 2 (1101)_2 (1101)2

  2. 定义一个基底 b a s e base base = = = 2 2 2, a n s ans ans = = = 1 1 1.

  3. 从右至左遍历 b b b

    3.1 遇到 1 1 1, a n s ans ans = = = 1 1 1 ∗ * 2 2 2 = 2 2 2, b a s e base base = 2 2 2^2 22 = 4 4 4

    3.2 遇到 0 0 0, a n s ans ans 不变, b a s e base base = 4 2 4^2 42 = 16 16 16

    3.3 遇到 1 1 1, a n s ans ans = 2 2 2 ∗ * 16 16 16 = 32 32 32, b a s e base base = 1 6 2 16^2 162 = 256 256 256

    3.4 遇到 1 1 1, a n s ans ans = 32 32 32 * 256 256 256 = 8192 8192 8192, 遍历完毕 □ \square

  4. 输出 a n s ans ans = = = 8192 8192 8192

相信您现在已经掌握了整数快速幂了叭

二, 矩阵加速

1. 简介

这是一个很神奇的算法, 比如说给定你一个递推关系式, 然后要你求第 n n n ( n n n是很大的一个数) 项的值, 如果你纯粹递推的话, 肯定很慢, 但是用了矩阵加速之后就变得飞快.

比如说对于斐波那契数列, f [ n ] = f [ n − 1 ] + f [ n − 2 ] f[n]=f[n-1]+f[n-2] f[n]=f[n1]+f[n2], 要你求第 n n n 项的值

很多人也写过类似的博客, 是那种找规律一样的, 我不喜欢这样做, 下面我来谈一谈我的做法, 如何快速找到转移矩阵

首先进行一个简单的数学推导, 相信大家都看得懂, 以后碰到类似的也可以这样做, 很容易找到转移矩阵

( a n a n − 1 ) \begin{pmatrix} a_n & a_{n-1} \end{pmatrix} (anan1) = = = ( a n − 1 + a n − 2 a n − 1 ) \begin{pmatrix} a_{n-1} + a_{n-2} & a_{n-1} \end{pmatrix} (an1+an2an1) = = = ( a n − 1 a n − 2 ) \begin{pmatrix} a_{n-1} & a_{n-2} \end{pmatrix} (an1an2) ∗ * ( 1 1 1 0 ) \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} (1110) = = = ( a 2 a 1 ) \begin{pmatrix} a_2 & a_1 \end{pmatrix} (a2a1) ∗ * ( 1 1 1 0 ) n − 2 \begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix}^{n-2} (1110)n2

L A T E X L^AT_EX LATEX 的矩阵是真的难打(小声bb

式子推出来了, 剩下的矩阵快速幂一阵乱锤就行, 下面介绍矩阵快速幂

三, 矩阵快速幂

1. 简介

不知道各位学习过线性代数 没有, 矩阵是线代里面一个非常重要的知识, 如果有的童鞋没有学习或者过了太久忘了的话, 我可以帮您回顾一下相关的重要知识点

我实在不想打矩阵了, 这里无关紧要的就用文字描述一下吧
1. 加法运算

要求: 同型矩阵, 即两个矩阵的规模一样, 都是 m ∗ n m*n mn.

操作: 将两个矩阵对应位置的数相加即可

2. 减法操作

就是加法的逆运算, 不再赘述

3. 数乘操作

要求: 一个数乘以一个矩阵

操作: 将矩阵的每一个位置的元素都乘以这个数

4. 重头戏 , 敲重点, 矩阵乘法

矩阵乘以一个矩阵
要求: 矩阵A * 矩阵B, 必须要求, 矩阵A的规模为 m ∗ n m*n mn, 则 B的规模为 n ∗ k n*k nk, 即A的列数等于B的行数

操作: 如下图所示(打矩阵太累了

矩阵乘法满足结合律哟

有了上面的知识储备后, 你会发现, 又回到了最初的快速幂的问题, 只不过现在不是两个整数相乘, 而是两个矩阵相乘, 所以我们只需重载一下 ∗ * 运算符, 其他的与整数快速幂没有什么大的区别了

1. 数据结构
struct Mat {
    ll m[MAXN][MAXN];
    //构造函数, 将矩阵所有元素都初始化为0
    Mat() {
        memset(m, 0, sizeof(m));
    }
    
    //调用这个函数即可初始化为单位矩阵
    void build () {
        for (int i = 1; i <= n; i++) {
            m[i][i] = 1;
        }
    } 
};
单位矩阵就是左上角到右下角的对角线上的元素为1, 其他均为0的矩阵
重载运算符 ∗ *
Mat operator* (const Mat& a, const Mat& b) {
    Mat c;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                c.m[i][j] = c.m[i][j] + a.m[i][k] * b.m[k][j];
            }
        }
    }

    return c;
}
快速幂主体
while (k > 0) {
    if (k & 1) {
        ans = ans * mat;
    }
    mat = mat * mat;
    k >>= 1;
}
注意, 这里不能使用 *= 运算符, 因为没有重载

接下来就大功告成啦

码字不易, 如果这篇文章能帮助到您的话, 就请点个赞再走呗, 您的一个赞将是对我的极大鼓励, _!
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值