背景介绍
当我们计算一个底数为a的n次幂时,当n过大时,如果我们单纯让n个a相乘,也就是朴素算法,很可能会出现整数溢出的情况,这种做法的时间复杂度为O(n)。而快速幂做法的时间复杂度为O(),可以大大优化求解类似问题的效率。
基本思路
问题拆解
快速幂的基本思路是:当n为正整数时,n可以被划分为两类,即偶数与奇数。1.倘若n为偶数,那我们可以将所求转换为求
,即
;2.倘若n为奇数,我们可以将所求
转换为求
,显然n-1为偶数,进而回到指数为偶数的情况。对
我们又可以进行诸如此类的划分······最后我们将问题转换为指数的二进制分解,同时不断对底数进行平方处理,文字上理解比较抽象,我们不妨举个例子:
我们发现这样的分解最后将问题导向某种类似二进制的表示方法,那我们将结果以完整的二进制表示,有
这不就是咱们的二进制嘛,也就是说我们将指数n分解成了二进制的表示方法,就像这个105被分解成1101001,也就是1+8+32+64,那我们可以对这几个部分分别处理,当运算到1101001的最右端是,我们通过检查这个指数转换为二进制后的数字最后一位是不是1,来判断这个地方需不需要乘,然后将a不断平方,模拟进位。紧接着右移,继续遍历检查二进制位数上1的存在。最后输出结果。
代码实现
代码表现如下:
long long BinExp(long long a,long ,long long n){
long long r=1;
while(n){
if(n%2==1){
r=r*a%MOD;
}
a=a*a%MOD;
n=int(n/2);//n除以2并向下取整
}
return r;
}
一般在这个过程中,数字会比较大,所以需要不断进行取模处理。
那我们也可以把该过程简化为位与的做法:
long long BinExp(long long a,long long n){
long long r=1;
while(n){
if(n&1==1)
r=r*a;
a=a*a;
n=n>>1;//n的二进制右移一位
}
return r;
}
应用方面
以下有矩阵方面应用,当然如果足够熟练,应用的场景可以无限发散,所以以下列出最经典的矩阵应用:
斐波那契数列
背景:
斐波那契数列(Fibonacci sequence)是由意大利数学家莱昂纳多·斐波那契于1202年提出的,其定义为:F(n)=F(n−1)+F(n−2),初始条件为F(0)=0,F(1)=1。
这个原先是经典的递归问题,但我们可以用快速幂来优化这个过程。
基本思路:
那么我们如何优化呢,这个有点考验我们的线性代数功底。我们可以把公式写为:
那么根据这个我们再去进行递归,就把原先的二元递归变为了一元递归,要进行多次的递归,我们再利用快速幂思想,也就是:
对于我们就可以用快速幂做啦,对于c++来说没有直接的矩阵相乘的函数,我们需要提前定义矩阵乘法函数,也可以用类的封装做。以下是代码实现:
代码实现:
函数代码:
vector<vector<long long>> multiply(const vector<vector<long long>> &A,const vector<vector<long long>> &B,long long MOD){
int n=A.size()//这里我们默认A和B矩阵为方阵,取n为A和B的行列数
//如果A和B不为方阵,我们可以这样取A和B和行数和列数
/* int row=A.size();
int col=A[0].size();
因为二维矩阵A的形式一般是{{1,0,1},{1,1,1}},那么直接取A.size(),其实就是指行数,取A[0].size就是列数*/
vector<vector<long long>>C(n,vector<long long>(n,0));//初始化一个大小为n*n的结果矩阵C,初始值全设为0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
C[i][j]=(C[i][j]+A[i][k]*B[k][j]%MOD)%MOD;
}
}
}
return C;
}
//设置一个单位矩阵(类似于整数快速幂里的r=1)
vector<vector<long long>> identity(int n){
vector<vector<long long>> I(n,vector<long long>(n,0));//初始值为0
for(int i=0;i<n;i++) I[i][i]=1;//对角线全为1
return I;
}
//矩阵快速幂矩阵
vector<vector<long long>>matrix_pow(vector<vector<long long>>A,long long pow,long long MOD){
int n=A.size();
vector<vector<long long>>res=identity(n);
while(power>0){
if(power&1) res=multiply(res,A,MOD);
A=multiply(A,A,MOD);
power>>=1;
}
return res;
}
类的封装实现:
#include <iostream>
#include <vector>
using namespace std;
const long long MOD=1e9+7;
class Matrix{
public:
vector<vector<long long> > mat;
int rows,cols;
//构造函数
Matrix(int r,int c):rows(r),cols(c){
mat.assign(r,vector<long long>(c,0));
}
//单位矩阵构造函数(静态方法)
static Matrix identity(int n){
Matrix I(n,n);
for(int i=0;i<n;i++)I.mat[i][i]=1;
return I;
}
//运算符重载:矩阵乘法
Matrix operator *(const Matrix& other)const{
if (cols!=other.rows){
throw runtime_error("行列不对应");
}
Matrix res(rows,other.cols);
for(int i=0;i<rows;i++){
for(int j=0;j<other.cols;j++){
for(int k=0;k<cols;k++){
res.mat[i][j]=(res.mat[i][j]+this->mat[i][k]*other.mat[k][j]%MOD)%MOD;
}
}
}
return res;
}
//矩阵快速阵,只能用于方阵
Matrix pow(long long power)const{
if(rows!=cols)throw runtime_error("不是方阵");
Matrix result=Matrix::identity(rows);
Matrix base=*this;
while (power>0){
if(power&1)result =result*base;
base=base*base;
power>>=1;
}
return result;
}
//打印函数
void print()const{
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
cout<<mat[i][j]<<' ';
}
cout<<'\n';
}
}
};
int main(){
int n;
cin>>n;
Matrix fib(2,2),chushi(2,1);
fib.mat={{1,1},{1,0}};
chushi.mat={{1},{0}};
Matrix res=fib.pow(n-1);
res=res*chushi;
long long Fn=res.mat[0][0]%MOD;
cout<<Fn<<endl;
}
总的来说,快速幂可以帮我们在很多方面优化效率,尤其是遇到递归类型时,不妨从快速幂的角度思考一下。
6895

被折叠的 条评论
为什么被折叠?



