啥是快速幂
这篇文章是为了帮一个同学学习ksm写的
百度百科给的定义:
顾名思义,快速幂就是快速算底数的n次幂。其时间复杂度为 O(log₂N), 与朴素的O(N)相比效率有了极大的提高。
这是一种能够快速的计算出(a^b)的值的算法
如果暴力计算(a^b)需要把a乘自己b次,当b太大时,就会超时。
使用ksm就只用log₂b次,计算次数大大减小。
原理:
-
将a乘自己a*a=(a^2),再做同样的操作(a^2 *a^2=a^4);所以a自乘n次后就是(a^2n).
-
(a^x *a^y=a^(x+y))
-
对于b,只要它是一个正整数,都可以把它拆成若干个2的不同次幂相加。
如:11=(2^3+2^1+2^0)
而11的二进制就是1011,也就是计算机里所储存的形式,更方便去对于b进行上述拆分。
过程:
定义两个数,temp和ans;temp即为要进行自乘的数初始值为上文所说的a;ans初值为1,即为答案,(为什么是1呢,自己思考)。
while(b)//也就是当b大于0时
先判断b的最后一位是否为1
如果是就将ans*=temp;
if(b&1) ans=(1longlong*ans*temp);
关于 b & 1:
“&”就是“按位与”。
x & y 是二进制 x 和 y 的每一位分别进行“与运算”的结果。
与运算:即两者都为 1 时才会返回 1,否则返回 0。
那么 b & 1
b = 1011
1 = 0001
b&1 = 0001
因为 1的前面几位全部都是 0,
所以只有 b 二进制最后一位是 1 时,b & 1 才会返回 1。
挺巧妙的,并且很快。
之后将temp自乘,变成(a^2);
同时将b右移一位。
即:若b=11(10),二进制下b=1011(2);
右移一位就变成b=101(2);
temp=(1longlong*temp*temp);
b=>>1;
不断重复上述过程直到不满足while的条件b>0;
代码实现:
以luoguP1226为例
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll b,p,k;
ll ksm(ll a,ll b,ll z)//快速幂函数
{
//这里的temp可以直接用a来表示,不影响结果,可以自己想为什么
ll ans=1;
while(b)
{
if(b&1) ans=(1ll*ans*a)%z;
a=(1ll*a*a)%z//自乘
b>>=1;//右移
}
return ans;
}
int main()
{
scanf("%lld%lld%lld",&b,&p,&k);
printf("%lld^%lld mod %lld=%lld",b,p,k,ksm(b,p,k)%k);
return 0;
}