算法|数学与数论|快速幂和光速幂

数学与数论|快速幂和光速幂

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;
}

心有猛虎,细嗅蔷薇。再见了朋友~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值