快速幂的计算

板子题:Welcome - Luogu Spilopeliahttps://www.luogu.com.cn/problem/P1226要求a^b的值,当a和b很大时,计算a^b的时间复杂度为O(b),我们考虑一种方式,将时间复杂度降为O(log b)。

思路如下:

当要计算5^10时,我们可以将其拆成计算(5^8)*(5^2)的值。

因为5^8=*(5^2)*(5^2)*(5^2)

因此可以以此简化计算

10的二进制为1010, 10=(2^3)*1+(2^2)*0+(2^1)*1+(2^0)*0

此时可将问题转化为求10的二进制数

如果10的二进制中第 i 位(i从0开始,从右往左数)为1

那么说明该位需要参与幂的运算。

代码如下:

#include <iostream>
using namespace std;
typedef long long ll;

ll ans = 1;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    ll a, b, p; 
    cin >> a >> b >> p;
    ll n = a, b0 = b;

    while (b != 0) { //该循环用于计算b的二进制
        ll x = b % 2; 
        if (x) {  //如果第i位不为0 
            ans = (ans * n); 
        }
        n = (n * n);  //每次循环:n=n^2,n=n^4,n=n^8,n=n^16,……
        b /= 2;
    }

    cout << a << '^' << b0 << " mod " << p << "=" << ans % p << endl;
    return 0;
}

但是这个代码只能通过2个案例

原因是当a和b非常大时,a^b超出了long long 的范围

此时需要用到一个性质: (a*b)%c=( (a%c)*(b%c) )  %c

证明如下:

设x=a%c, 则a=i*c+x      i为任意正整

设y=b%c, 则b=j*c+y      j为任意正整数

所以a*b=(i*c+x)*(j*c+y) = i*j*c*c+i*y*c+j*x*c+x*y=(i*j*c+i*y+j*x)*c+x*y

设 k=i*j*c+i*y+j*x ,则a*b=k*c+x*y

(a*b)%c=(k*c+x*y)%c=0+(x*y)%c=( (a%c)*(b%c) )  %c

因此 (a^n)%c=( (a%c)^n )  %c

ac代码如下:

#include <iostream>
using namespace std;
typedef long long ll;

ll ans = 1;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    ll a, b, p; 
    cin >> a >> b >> p;
    ll n = a, b0 = b;
    while (b != 0) {
        ll x = b % 2;
        if (x) {
            ans = (ans * n)%p ;  //这样就可以保证不溢出
        }
        n = (n * n)%p;  //这样就可以保证不溢出
        b /= 2;
    }
    cout << a << '^' << b0 << " mod " << p << "=" << ans % p << endl;
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值