快速幂是一种高效的指数运算方法,通过指数折半或二进制位运算减少计算次数。它的核心思想是利用二进制表示法或指数折半来加速计算,从而避免大量的循环操作。
快速幂可有多种方法实现,最主要的两种是,二进制、指数折半方法实现。
1、BF暴力解法:
谈到算法的起源与优化,首先就要想到他们的原身-暴力解法,这是根,亦是魅力。
#include "bits/stdc++.h"
using namespace std;
#define ll long long
ll Pow(int base, int exponent){
int result = 1;
for(int i = 0; i
一招遍历走天下。
但是如果是1万次方,真的要循环1万次吗?虽然可行,但极度浪费运算时间。
于是提出了二进制法,将指数转化为二进制,通过位运算进行计算-大神解释。
2、快速幂 二进制
相信根据大神解释,已经了解基本运作类型,以下为代码:
1、速记、背诵版:
#include "bits/stdc++.h"
using namespace std;
#define ll long long
ll Pow(int base,int exponent){ // base基数、exponent指数
ll result = 1;
// 位运算
while(exponent>0){
if(exponent&1) // exponent与最低位进行运算
result *= base;
base *= base;
exponent>>=1; // 右移一位
}
return result;
}
int main(){
// 如何求快速幂,对指数进行取余,
ll num = Pow(2,4);
cout<
2、详细解释版:
// 包含一个非标准的头文件,它整合了几乎所有标准库的头文件
// 在竞赛等场景使用较为方便,但在正式项目中不推荐,因为会增加编译时间
#include "bits/stdc++.h"
// 使用标准命名空间 std,这样在后续代码中使用标准库的类和函数时无需加 std:: 前缀
using namespace std;
// 定义一个宏,将 ll 作为 long long 类型的别名
// 后续代码中使用 ll 就相当于使用 long long,简化代码书写
#define ll long long
// 定义一个函数 Pow,用于计算 base 的 exponent 次幂
// base 是底数,即要进行乘方运算的数
// exponent 是指数,即表示乘方的次数
ll Pow(int base, int exponent) {
// 初始化一个 long long 类型的变量 result 为 1
// 因为任何数的 0 次幂为 1,后续通过累乘来得到最终的幂结果
ll result = 1;
// 开始一个 while 循环,只要指数 exponent 大于 0 就继续循环
// 该循环是实现快速幂算法的核心部分
while (exponent > 0) {
// 使用按位与运算符 & 来判断 exponent 的二进制表示的最低位是否为 1
// 如果 exponent 的最低位是 1,那么 exponent & 1 的结果为 1,条件成立
// 例如,若 exponent 为 5(二进制 101),101 & 001 的结果为 1
if (exponent & 1) {
// 当 exponent 的最低位为 1 时,说明当前这一位对应的 2 的幂次需要参与结果的计算
// 将当前的 base 乘到结果 result 中
result *= base;
}
// 将 base 进行平方操作
// 例如,第一次循环时 base 是原始值,第二次循环时 base 变为 base^2,第三次循环时 base 变为 base^4,以此类推
base *= base;
// 将 exponent 右移一位,相当于将 exponent 除以 2
// 右移操作在二进制层面上把所有位向右移动一位,最右边的位被丢弃,左边补 0
// 例如,若 exponent 为 5(二进制 101),右移一位后变为 2(二进制 10)
exponent >>= 1;
}
// 当 exponent 变为 0 时,循环结束,此时 result 即为 base 的 exponent 次幂的结果,将其返回
return result;
}
// 主函数,程序的入口点,程序从这里开始执行
int main() {
// 调用 Pow 函数计算 2 的 4 次幂,并将结果存储在 long long 类型的变量 num 中
ll num = Pow(2, 4);
// 使用 cout 输出计算得到的结果 num,并换行
// cout 是标准输出流对象,用于向控制台输出信息
cout << num << endl;
// 主函数返回 0,表示程序正常结束
// 在 C++ 中,主函数返回 0 通常表示程序成功执行完毕
return 0;
}
快速幂演变至今,不可能只有这么一种方法,接下来介绍另一种方法快速幂-指数折半-大神解释
3、快速幂 指数折半
相信根据大神解释,已经了解基本运作类型,以下为代码:
1、速记、背诵版本:
#include "bits/stdc++.h"
using namespace std;
#define ll long long
ll Pow(int base, int exponent){
ll result = 1;
while(exponent>0){
// 分奇、偶
if(exponent%2==1){// 奇数
exponent -= 1;
exponent /= 2;
result *= base;
base *= base;
}else{ // 偶数
exponent = exponent/2;
base *= base;
}
}
return result;
}
int main(){
// 如何求快速幂,对指数进行取余,
ll num = Pow(2,4);
cout<
2、详细解释版:
// 包含几乎所有标准库的头文件,在竞赛等场景中方便使用各种标准库功能
#include "bits/stdc++.h"
// 使用标准命名空间,这样在代码中使用标准库的类和函数时可以省略 std:: 前缀
using namespace std;
// 定义一个宏,将 ll 作为 long long 类型的别名,方便后续使用长整型
#define ll long long
// 定义一个函数 Pow,用于计算 base 的 exponent 次幂
// base 是底数,即要进行乘方运算的数
// exponent 是指数,即表示乘方的次数
ll Pow(int base, int exponent){
// 初始化结果变量 result 为 1,因为任何数的 0 次幂为 1,后续用于累乘得到最终结果
ll result = 1;
// 当指数 exponent 大于 0 时,继续循环进行幂运算
while(exponent>0){
// 根据指数的奇偶性进行不同处理
// 分奇、偶
if(exponent%2==1){// 奇数
// 若指数为奇数,先将指数减 1 变为偶数
exponent -= 1;
// 再将指数除以 2
exponent /= 2;
// 由于当前指数为奇数,此时需要将当前的 base 乘到结果 result 中
result *= base;
// 将 base 平方,为下一次计算做准备,相当于让 base 的幂次翻倍
base *= base;
}else{ // 偶数
// 若指数为偶数,直接将指数除以 2
exponent = exponent/2;
// 将 base 平方,为下一次计算做准备,相当于让 base 的幂次翻倍
base *= base;
}
}
// 循环结束后,result 即为 base 的 exponent 次幂的结果,将其返回
return result;
}
// 主函数,程序的入口点
int main(){
// 调用 Pow 函数计算 2 的 4 次幂,并将结果存储在变量 num 中
// 如何求快速幂,对指数进行取余,
ll num = Pow(2,4);
// 输出计算得到的结果
cout<
4、竞赛
要知道,直接调用标准库函数,代码简洁。也就是 cmath 中的 pow()。
但是 pow() 存在两个缺点
所以不推荐使用cmath中的pow()。而推荐使用cmath。
1、数的幂次
题目描述
给定三个正整数 N,M,PN,M,P,求 NMmodpNMmodp。
输入描述
第 11 行为一个整数 TT,表示测试数据数量。
接下来的 TT 行每行包含三个正整数 N,M,PN,M,P。
1≤T≤1051≤T≤105,1≤N,M,P≤1091≤N,M,P≤109。
输出描述
输出共 TT 行,每行包含一个整数,表示答案。
输入输出样例
示例 1
输入
3 2 3 7 4 5 6 5 2 9
输出
1 4 7
#include
#define ll long long // 必须要开到long long,否则会报错
using namespace std;
ll quick(ll n, ll m, ll p){
ll res = 1;
while(m){
if(m&1ll) res=(res*n)%p; // 1ll 也可以写成1,1ll代表为long long
n=(n*n)%p;
m>>=1;
}
return res;
}
int main()
{
int t;
cin>>t;
while(t--){
ll n,m,p;
cin>>n>>m>>p;
cout<
2、小数第n位
题目描述
我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数。
如果我们把有限小数的末尾加上无限多个 0,它们就有了统一的形式。
本题的任务是:在上面的约定下,求整数除法小数点后的第 nn 位开始的 3 位数。
输入描述
输入一行三个整数:a b na b n,用空格分开。aa 是被除数,bb 是除数,nn 是所求的小数后位置(0
输出描述
输出一行 3 位数字,表示:aa 除以 bb,小数后第 nn 位开始的 3 位数字。
输入输出样例
示例
输入
1 8 1
输出
125
思路:
本题的主要思路是,通过乘10,把小数一个接一个的拉上来。并通过取模削减数字,防止溢出。
最重要的是,取模对本题中的除法运算没影响。
#include "bits/stdc++.h"
#define ll long long
using namespace std;
// getNum 通过快速幂-二进制
ll getNum(ll a, ll b, ll p){ // a底数,b指数,p除数
ll res = 1;
while(b){ // b是位数
if(b&1) res=(res*a)%p;
// a*=(a%p); -> 这样是错的,会导致快速幂出错
a = a*a%p;
b>>=1; // 右移一位
}
return res;
}
int main()
{
// 输入数字
ll a,b,n;
cin>>a>>b>>n; // a是除数、b是被除数、n是位数
// 进入这一步之前,有一点需要理清,取余不影响小数计算结果。
a = a*getNum(10,n-1,b); // 获得第n位的前一位。
a = a%b;
// 第二核心
for(int i=0; i<3; ++i){ // 取3位
a*=10;
cout<
3、堆的计数
好遗憾,这道题笔者咱无法解答,待后续有能力之后,在解决。
本题涉及了(组合数、费马小定理、逆元、阶乘、动态规划...),后续解决在给出答案
d堆的计数
笔者感悟:
在这么整理多篇算法博客的途中,我发现算法难,大多时候不是因为你不会这道题,而是因为读不懂答案!我准备用带入法测试(普通题目:取一个数字测试。递归:画图法),后续博客发表测试效果。
借鉴博客/网站:
1、蓝桥官网
2、快速幂算法 超详细教程
3、费马小定理证明(博主简直是个天才)
4、ACM数论之旅6---数论倒数,又称逆元(我整个人都倒了( ̄﹏ ̄))
5、乘法逆元的作用(逆元的作用是什么)