数学与数论|快速幂和光速幂
1.快速幂(递归)
2.快速幂(二进制)
3.快速幂(取模)
4.光速幂(根号分治)
5.龟速乘
心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。
快速幂,二进制取幂(Binary Exponentiation,也称平方法),是一个在O(logn)的时间内计算an的小技巧,而暴力计算需要O(n)的时间。
在a,n很大的情况下,暴力计算不适用,但这种情况会要求输出对mod取模的答案。所以我们需要一种方法,既能保证答案在0~mod-1之内,又能使时间复杂度在可接受范围内。而快速幂则是基于分治的思想,用于解决这种问题的算法。
快速幂(递归)
实践代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int qpow(int a,int b){
if(b==0) return 1;
if(b==1) return a;
int c = qpow(a,b/2);//递归的思想
if(b%2==1) return c*c*a;//b是奇数,除以2会向下取整
else return c*c;
}
快速幂(二进制)
实践代码:
int qpow(int a,int b){
int res = 1;
while(b){
if(b&1) res*=a;//检查二进制当前位是否有1
a*=a;//递推计算
b>>=1;
}
return res;
}
快速幂(取模)
注意:
因为一般题目让我们所求的幂元很大,但其实就是在二进制的基础上增加了取模运算。
实践代码:
const int mod = 1e9 +7;//(题目给定不同模时,替换就行)
int qpow(int a,int b){
int res = 1;
while(b){
if(b&1) res*=a,res%=mod;//检查二进制当前位是否有1
a*=a;a%=mod;
b>>=1;
}
return res%mod;
}
题目:
给你三个整数a,b,p,求 ab mod p。
输入描述:
输入只有一行三个整数,分别代表a,b,p。
输出描述:
输出一行一个字符串a^b mod p=s
,其中a,b,p分别为题目给定的值,s为运算结果。
示例1
输入
2 10 9
输出
2^10 mod 9=7
备注
样例解释
2^10 = 1024,1024 mod 9 = 7。
数据规模与约定
对于100%的数据,保证0≤a,b≤231,a+b>0,2≤p≤231。
实践代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
int qpow(int a,int b,int p){
int res = 1;
while(b){
if(b&1) res*=a,res%=p;//检查二进制当前位是否有1
a*=a,a%=p;
b>>=1;
}
return res%p;
}
void solve(){
int a,b,mod;cin>>a>>b>>mod;
cout<<a<<'^'<<b<<" mod "<<mod<<'='<<qpow(a,b,mod)<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
while(t--){
solve();
}
return 0;
}
光速幂,适用条件:底数和模数都固定,幂次改变,且查询次数特别多
虽然快速幂O(logn)的速度已经很快了,但不免有些黑心出题人把查找次数出到1e7。这样我们用快速幂O(Tlogn)是过不了的,这个时候就要用到快速幂的铁子光速幂了。
光速幂(根号分治)
补充思路:
—— zsyz_ZZY《光速幂学习笔记 & P1226 【模板】快速幂||取余运算》
这里再精进一点,即把n直接转换成mod,因为mod是n的最大边界(因为n总要对mod取模),解决边界问题。
实践代码:
const int N = 1e5 +10;
const int mod = 1e9 +7;
int sm = sqrt(mod);//根号mod
int l[N],h[N];
void lpow(int x){//底数x
l[0] = h[0] = 1;
for(int i=1;i<=sm;i++) l[i] = l[i-1]*x%mod;
for(int i=1;i<=sm;i++) h[i] = h[i-1]*l[sm]%mod;
}
void solve(){
int x,n;cin>>x>>n;//底数x和查询次数n
lpow(x);
while(n--){
int c;cin>>c;//查询x的幂c
cout<<h[c/sm]*l[c%sm]%mod<<' ';
}
}
题目:
给出正整数x和n个正整数ai,求xa mod p。
输入描述:
第一行,两个正整数x,n。
第二行,n个正整数ai。
输出描述:
一行n个正整数,分别表示xa mod p。
示例1
输入
2 3
1 2 3
输出
2 4 8
备注
数据规模与约定
对于100%的数据,1 ≤ n ≤5 x 106,1 ≤ x,ai < p ,p = 998244352
实践代码:
const int N = 1e5 +10;
const int mod = 998244352;
int sm = sqrt(mod);
int l[N],h[N];
void lpow(int x){//底数x
l[0] = h[0] = 1;
for(int i=1;i<=sm;i++) l[i] = l[i-1]*x%mod;
for(int i=1;i<=sm;i++) h[i] = h[i-1]*l[sm]%mod;
}
void solve(){
int x,n;cin>>x>>n;//底数x和查询次数n
lpow(x);
while(n--){
int c;cin>>c;//查询x的幂c
cout<<h[c/sm]*l[c%sm]%mod<<' ';
}
}
补充:
龟速乘
题目:
求 a 乘 b 对 p 取模的值,其中 1≤a,b,p≤1018。
输入描述:
第一行a,第二行b,第三行p。
输出描述:
一个整数,表示a×b mod p的值。
示例1
输入
2
3
9
输出
6
实践代码:
int qmul(int a,int b,int p){//龟速乘
int ans=0;
while(b){
if(b&1) ans+=a,ans%=p;
a+=a,a%=p;
b>>=1;
}
return ans%p;
}
void solve(){
int a,b,p;cin>>a>>b>>p;
cout<<qmul(a,b,p)<<endl;
}
心有猛虎,细嗅蔷薇。再见了朋友~