目录
一.问题引入
试想,如果我们想要计算一个数字的n次方,可以如何计算?
首先很容易想到以下方法:
#include<iostream>
using namespace std;
int main()
{
long long n,x;
cin>>n>>x;
long long res=1;
for(int i=1;i<=x;i++)
{
res=res*n;
}
cout<<res<<endl;
}
上述方法的时间复杂度为o(x),而且当res>2^31时,最后结果不能被输出表示出来
于是有了下面的思考:
如果我们要算5^8,是不是可以这样计算:(5*5)*(5*5)*(5*5)*(5*5)
(5*5)*(5*5)*(5*5)*(5*5)
(5*5)*(5*5)*(5*5)*(5*5)
两两相乘,只需要乘以3次就可以得到结果
#include<iostream>
using namespace std;
int main()
{
int a=5;
a=a*a; //5*5
a=a*a; //(5*5)*(5*5)
a=a*a; //(5*5)*(5*5)*(5*5)*(5*5)
cout<<a<<endl;
}
这样一来,我们的时间复杂度就从o(x)降到了o(log(x))
但还是解决不了我们的问题,考虑以下方法:
二.快速幂
由进制的转换我们知道,任何正整数都能写成2^n的形式
5=2^2+2^0;
7=2^2+2^1+2^0;
10=2^3+2^1
通过以上基础知识,我们就可以将快速幂的过程转换为在求每个数的二进制时,进行2^n的相乘,
举个例子: 9的二进制是 1001
5^9 =5^(2^3+2^0)
1 0 0 1
2^3 2^2 2^1 2^0
模拟过程:
res 5^(2^0) 5^(2^0)*5^(2^3)
x 4 2 1 0
n 5^(2^1) 5^(2^2) 5^(2^3) 5^(2^4)
但是问题又来了,int 最多存2^15,long long 最多存2^31, 即使是最小的2^(10^8)也不能被存储记录下来,所以,快速幂的题目一般都是会让我们求a^b后的余数,即 (a^b)%p (p的范围为(0,2^31))
我们可以在算出结果a^b后再%p来求得结果,但是a^b的结果一般十分巨大,不能被存储下来,
由于模的基本运算性质:
我们分析出可以在边计算a^b的过程中%p,结果是不变的
以板子题:https://www.luogu.com.cn/problem/P1226为例,给出模版代码:
#include<iostream>
using namespace std;
long long a,x,p,n,m;
long long res=1;
//快速幂
int main()
{
cin>>a>>x>>p;
//模拟二进制的算法
n=a,m=x;
while(m!=0)
{
if(m%2==1) res=(res*n)%p; //为奇数的话,二进制的最后一位是1,反之为0
m=m/2;
n=(n*n)%p;
}
res=res%p;
printf("%lld^%lld mod %lld=%lld",a,x,p,res);
return 0;
}
时间复杂度为log(o(x)) <注意这里的x指的都是指数!>
三.运用场景
快速幂常用于某些题目的计算优化,主要还是算法思想和背板子。