详解快速幂(附习题)

模运算

定义:设 a , b ∈ Z a,b\in Z a,bZ b > 0 b>0 b>0,如果 q , r ∈ Z q,r\in Z q,rZ满足 a = q b + r a=qb+r a=qb+r 0 ≤ r < b 0\le r<b 0r<b则称 a a a b b b等于 r r r记作
a   m o d   b = r a\space mod \space b=r a mod b=r

负数取模

a a a不断的加上 b b b知道结果大于 0 0 0,这个结果就是模运算的结果

模运算的性质
  • b ∣ a ⇔ a   m o d   b = 0 b\mid a\Leftrightarrow a\space mod\space b=0 baa mod b=0
  • ( a + b ) m o d   n = ( a   m o d   n + b   m o d   n ) m o d   n (a+b)mod\space n=(a\space mod \space n+b\space mod\space n)mod\space n (a+b)mod n=(a mod n+b mod n)mod n
  • ( a − b ) m o d   n = ( a   m o d   n − b   m o d   n ) m o d   n (a-b)mod\space n=(a\space mod \space n-b\space mod\space n)mod\space n (ab)mod n=(a mod nb mod n)mod n
  • ( a × b ) m o d   n = ( a   m o d   n × b   m o d   n ) m o d   n (a\times b)mod\space n=(a\space mod \space n\times b\space mod\space n)mod\space n (a×b)mod n=(a mod n×b mod n)mod n
  • 除法不具有这样的性质
    在快速幂取模中每一步计算都可以进行一次取模,以简化中间结果,防止溢出

快速幂

为什么要用快速幂

计算 a b   m o d   p a^b\space mod\space p ab mod p,其中 a , b a,b a,b是非常大的数。这时如果将 a b a^b ab一个一个累乘计算的话会很慢,尽管他的时间复杂度是 O ( n ) O(n) O(n),并且这样一个大的数字很难存储在计算机中( i n t , l o n g l o n g int,long long int,longlong都会溢出),这时就需要快速幂来解决这个问题。

R S A RSA RSA加密中也使用了快速幂算法

原理

快速幂其实就是利用了分治的思想,将 a b a^b ab分成 a b / 2 a^{b/2} ab/2 a b / 2 a^{b/2} ab/2两个部分,一直细分,直到幂次 b = 1 b=1 b=1,再将这些结果累乘起来,并且利用模运算的性质,在累乘过程中不断取模,防止溢出,最后就可以得到最后的答案。

递归实现

递归的思路非常简单,常规。

typedef unsigned long long ull;
ull a, n, p;
ull q_pow(ull a, ull n,ull p) {
    if (n == 1) return a;
    ull c = q_pow(a, n >> 1, p) % p;//右移一位代表除二,位运算符在计算机中效率较高
    if (n & 1) return c * c % p * a % p;
    return c * c % p;
}
非递归实现

非递归需要利用到二进制的知识。对于任意一个十进制数字我们都可以把它写成二进制累加的形式,下面我们举个例子说明:
5 = 2 2 + 2 0 5=2^2+2^0 5=22+20
所以,我们就可以把幂 b b b写成一个二进制累加的形式,再利用幂的运算规则,就可以将 a a a写成一串数的乘积,
3 5 = 3 2 2 + 2 0 = 3 2 2 × 3 2 0 3^5=3^{2^2+2^0}=3^{2^2}\times 3^{2^0} 35=322+20=322×320
这些乘数的幂次都对应着 b b b的一个二进制位,我将幂次写成二进制的形式 3 ( 101 ) 2 = 3 ( 100 ) 2 + ( 001 ) 2 3^{(101)_2}=3^{(100)_2+(001)_2} 3(101)2=3(100)2+(001)2,每个二进制位为 1 1 1的位置,就是上式中乘数的幂次。
写代码时,我们就可以遍历幂次 b b b的二进制位,等于 1 1 1的就将其乘起来,并且不断取模保证不溢出,最后遍历完 b b b的二进制位,循环结束,得到的乘积就是结果。

代码实现
typedef unsigned long long ull;
ull a, b, p;
ull q_pow(ull a, ull b, ull p) {
    ull res = 1;
    while (b > 0) {
        if (b & 1) res = res * a % p;
        a = a * a % p;//b每右移一位,a对应的也要平方
        b >>= 1;
    }
    return res;
}

洛谷模板练习

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IdlePerson.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值