快速幂

news/2025/1/22 20:46:21/文章来源:https://www.cnblogs.com/Weekoder/p/18237773

大家好,我是Weekoder!

今天的内容是快速幂!(实际上是为了讲矩阵快速幂赶出来的嘻嘻

\[\texttt{Part 1 用处} \]

快速幂,顾名思义就是快速地计算出某个数的幂,形如 \(a^n\)

\[\texttt{Part 2 思想} \]

为什么普通的幂运算慢?假设要计算 \(a^n\),则需要拆分成 \(a\times a\times\cdots\times a\times a\),运算 \(n\) 次,复杂度为 \(O(n)\)。当 \(n\) 很大时,这个算法明显就不行了。那要怎么优化呢?我们先来看一个简单一点的例子:当 \(n\)\(2\) 的幂时,可以怎么做呢?假设 \(n\)\(32\),可以这样计算:

\[\begin{aligned} a^1\times a^1=a^2 \\ a^2\times a^2=a^4 \\ a^4\times a^4=a^8 \\ a^8\times a^8=a^{16} \\ a^{16}\times a^{16}=a^{32} \end{aligned} \]

注意:\(a^x\times a^y=a^{x+y}\)

可以发现,这样只计算了 \(5\) 次,相比于朴素算法的 \(32\) 次,将时间复杂度优化到了 \(O(\log n)\)。这其实是倍增的原理,相比于一个一个乘 \(a\),不如将 \(a\) 的数量翻倍乘。

那如果 \(n\) 不是 \(2\) 的幂呢?比如,\(n=105\) 的时候,该怎么办呢?虽然 \(105\) 不是 \(2\) 的幂,但是我们发现 \(105\) 可以拆分成 \(2\) 的幂之和,像这样:

\[105=1+8+32+64 \]

于是,我们可以把 \(a^{105}\) 拆分一下:

\[a^{105}=a^{1+8+32+64}=a^1\times a^8\times a^{32}\times a^{64} \]

我们在刚刚提到过,计算 \(n\)\(2\) 的幂的情况是很容易的,所以我们只需要将它们相乘即可。

这个问题的关键在于,怎样将一个数拆分成 \(2\) 的幂之和?我们来看一下他们在二进制下的样子:

可以看到,\(105\) 的二进制中有 \(4\)\(1\),而 \(2\) 的幂的数都只有一个 \(1\),并且刚好和 \(105\) 的四个 \(1\) 位置一样。所以,只要将 \(105\) 二进制中的 \(1\) 拆开,就能得到 \(2\) 的幂的数字是哪些了。

而因为一个数 \(n\) 的二进制最多只有 \(\log n\) 位,所以时间复杂度为 \(O(\log n)\)

\[\texttt{Part 3 实现} \]

就决定是你了!快速幂模板!

先上代码:

#include <bits/stdc++.h>
using namespace std;typedef long long ll;ll expow(ll a, ll n, ll p) {ll r = 1;while (n) {if (n & 1) r = r * a % p;a = a * a % p, n >>= 1;}return r;
}int main() {ll a, b, p;cin >> a >> b >> p;cout << a << "^" << b << " mod " << p << "=" << expow(a, b, p);return 0;
} 

输入和输出就不用我讲了,重点是 \(\text{expow}\) 函数。我把快速幂函数提取出来(先不取模):

typedef long long ll;ll expow(ll a, ll n) {ll r = 1;while (n) {if (n & 1) r *= a;a *= a, n >>= 1;}return r;
}

比如计算 \(7^{105}\)

首先,我们用一个 \(\color{yellow}\texttt{r}\color{#000000}\texttt{esult}\) 来存储结果,初始时为 \(1\)。接着,有一个 \(\text{while}\) 循环,其实就是遍历 \(n\) 在二进制下的每一位。如果当前这一位是 \(1\),即 \(n\)\(1\) 按位与的结果为 \(1\),则可以拆分,将 \(r\) 乘上 \(a\)。每过一位,\(a\) 就变为 \(a^2\),即模拟倍增的过程。然后还要将 \(n\) 除以 \(2\),用位运算表示就是右移一位,获取下一位。最后返回结果 \(r\)。可以辅助图片理解。

这样就可以用 \(O(\log n)\) 的速度计算 \(a^n\) 了。

小扩展:幂取模

即计算 \(a^n \bmod p\)

只需要在快速幂的模板里稍微改动一下。在做乘法运算时,顺带取模就行了。

幂取模模板代码如下:

typedef long long ll;ll expow(ll a, ll n, ll p) {ll r = 1;while (n) {if (n & 1) r = r * a % p;a = a * a % p, n >>= 1;}return r;
}

\[\texttt{Part 4 小结} \]

综上所述,二进制快速幂的核心就是这些了。当然,快速幂除了计算 \(a^n\) 以外,还有很多用处等着你去发现。

再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/721534.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript中的async/await

async/await是什么? async 是一个修饰符,async 定义的函数会默认的返回一个Promise对象resolve的值,因此对async函数可以直接进行then操作,返回的值即为then方法的传入函数。await 也是一个修饰符,await 关键字 只能放在 async 函数内部, await关键字的作用 就是获取 Prom…

Mysql 8.4.0 结合 Docker 搭建GTID主从复制,以及传统主从复制

注意:本教程不适用旧版本,Mysql 8.4.0 和 旧版本,主从复制相关命令有所变化,具体区别请看文末参考 软件版本 Docker:26.1.3 Mysql:8.4.0GTID主从复制 1.准备主从两台服务器 2.两台服务器分别创建DockerCompose文件 services:mysql:image: mysql:8.4.0ports:- "3306:…

Vue Router 4与路由管理实战

这篇文章介绍了如何在Vue.js应用中利用Vue Router实现单页面应用的路由管理,包括配置路由、导航守卫的使用、路由懒加载以优化性能以及动态路由的实现方法,旨在提升用户体验和应用加载效率title: Vue Router 4与路由管理实战 date: 2024/6/7 updated: 2024/6/7 excerpt: 这篇…

Body SweptSolid CompositeCurve Geometry

Body SweptSolid CompositeCurve Geometry 下图显示了应用此概念时使用的泛型类和关系。此外,概念可能对通用或标准化的行业实践和场景具有特别重要的意义。对于这些特定的使用场景,下表显示了用户可能采用的一般使用模式的推荐列表。 #####################################…

设备树学习

设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS(DeviceTree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、 IIC 接口上接了哪些设备、 SPI 接口上接了哪些设备等等。具体如下图…

平稳交付 20+ 医院,卓健科技基于 OpenCloudOS 的落地实践

本文将会阐述卓健科技运用 OpenCloudOS 的背景情况,深入探究其背后的缘由以及详细的实践流程。导语:随着数字化转型于各个行业领域当中持续地深入推进,充当底层支撑的操作系统正发挥着愈发关键且重要的作用。卓健科技把 OpenCloudOS 当作首要的交付系统,达成了项目交付速度…

机器学习笔记(2): Logistic 回归

Logistic 回归是线性回归中一个很重要的部分。 Logistic 函数: \[\sigma(x) = \frac {L} {1 + \exp(-k(x - x_0))} \]其中:\(L\) 表示最大值 \(x_0\) 表示对称中心 \(k\) 表示倾斜度一般来说,都将 \(L\) 设为 \(1\),而 \(k\) 和 \(x_0\) 在参数中控制。认为特征只有一个,那…

AI智能助手(web端和h5端)

需要在平台右下角设置一个图标,点击后可弹出智能助手弹框,同时不影响平台其他操作,支持流式文本(sse)、图文回复展示 web端 ,效果如下图: h5端 效果:vue3 + element-ui 实现 仿AI智能机器人助手h5, 支持流式文本(sse)、图文回复展示 web端源码:https://github.c…

dos窗口中关于目录和文件的操作

1 将输入内容保存到文件 dir > c:\1.txt 2 列出当前目录的所有文件 dir /a-d 2.1 列出当前目录的所有文件 dir /a-d /d 2.12列出当前目录及子目录下的所有文件 dir /a-d /d /s 3 列出当前目录下的所有目录 dir /ad /d /s 3.1 列出当前目录下的所有目录 dir /ad /d