快速幂

快速幂顾名思义就是速度很快的求幂的算法嘛,在一些大数据情况下使用快速幂可以节省大量的时间(用来暴力),当然这是调侃。举个例子:比如你要算A^7,你可以使用O(N)的算法  A*A*A*A*A*A*A,这也不是很慢,但是当你算A^10000000000000时你就会想:“woc,这tm不得算死我,电脑都不想算了”,这时我们就要用到快速幂了。

在这个高效的算法中我们把A的指数写成二进制的形式 A^7=A^111(2),此时我们令 ans=1,tot=A,N=7就有了下面的代码:

while(N)
{
  if(N%2==1)
  ans*=tot;
  tot*=tot;
  N>>=1;//C++中用位运算会更快一些
}

上面的代码运行过程写出来就是这样的:ans=1;tot=A; 

N=7 ;N%2=1;   ans*=tot; 所以ans=A; tot*=tot; 所以tot=A^2

然后 N/=2;N=3; N%2=1;ans*=tot; 所以 ans=A*A^2=A^3 ; 又tot*=tot; 所以tot=A^4

然后N/=2;N=1;N%2=1;ans*=tot; 所以ans=A*A^2*A^4=A^7;又tot*=tot; 所以tot=A^8

然后N/=2;N=0;算法结束  

是不是很巧妙呢,实际上用的乘法次数是 6次你可能觉得,那个A^7=A*A*A*A*A*A*A,不也是用了6次乘法吗有什么区别?

事实上这里的快速幂算法的时间复杂度是log2(n)(表示以2为底n的对数) 的复杂度,还有一个系数,大约是2 实际上计算次数就是 2*log2(n) 而普通的连乘计算的复杂度是n 乘法计算次数是n-1, (表示以2为底n的对数) 的复杂度,还有一个系数,大约是2 实际上计算次数就是 2*log2(n) 而普通的连乘计算的复杂度是n 乘法计算次数是n-1,这样在n很小时差别不大,但随着n的增长差距会迅速扩大,例如 n=1024时 普通方法得计算1023次乘法,但快速幂最多(因为当上面的程序执行时N的中间结果为偶数那么  ans*=tot,将不会被执行,故实际的计算次数要小于 2*log2(n))只算2*log2(n) =20次乘法,是不是很快!

但是为什么呢?好像还有点不懂。

实际上A^7=A^1*A^2*A^4这样每次计算乘法乘的因子都是递增的,而且还是指数递增,还有这些因子是可以递推产生的就是可以利用上次的计算每次平方就可以了,这中其实是使用的二进制的思想,因为任意一个数都可以,表示成二进制,故 A^N以定可以写成

A^(一个二进制数如101010)=A^(100000)*A^(00000)*A(1000)*A^(000)*A^(10)*A^(0)=A^(2^5)*A^(2^3)*A^(2^1)

而我们的tot其实是一个数列 A^1,A^2,A^4,A^8,A^16........即A^(2^0),A^(2^1),A^(2^2),A^(2^3),A^(2^4).......................注意到他的指数都是二进制的位权(不知道是不是这个名词,就像十进制的位权是 1 10 100 1000 10000,一样如1243=1*1000+2*100+3*10+4*1;而二进制的1011 是 1*2^(3)+0*2^(2)+1*2^(1)+1*2^(0) 这样是不是应该理解位权了呢?)实际上任何一个A^N都可以写成这个数列的某些项的乘积,因为N始终都可以表示成二进制,而把N表示成二进制后如果某项为1则说明需要乘上tot否则不用乘上tot

于是就有了上边的代码

学完了知识当然要进行练习了这里给出一道洛谷上的练习题  P1226取余运算||快速幂https://www.luogu.org/problem/show?pid=1226

这道题只是多了一步取余运算

直接上代码

#include<iostream>
using namespace std;
long long b,k,p;//long long型的变量
long long work(long long a,long long b,long long c)//快速幂函数
{
    long long tt=a,ans=1;
    while(b)
    {
        if(b%2) ans=tt*ans%c;//如果是奇数就乘上外带每步取模
        tt=tt*tt%c;//做平方运算加每步取模
        b>>=1;//此处用右移而不是  /2  因为右移快一些
    }
    return ans%c;
}
int main()
{
    cin>>b>>k>>p;
    cout<<b<<"^"<<k<<" mod "<<p<<"="<<work(b,k,p);//苛刻的输出要求,注意  mod  前后的空格(刚开始被坑了TAT)
    return 0;
}

 感谢CSDN的朋友hikean(我看了他的博客学会了的快速幂)

原文地址:https://www.cnblogs.com/wty20010315/p/6984348.html