快速幂,是根据幂的二进制最后一位0或1来加速进行乘法运算。
例如,在求5^19时,按照普通的求法就是要19个5相乘,这样虽然可以解出来,但当底数和指数都非常大时,这样计算的时间会很长,其时间复杂度为O(n)。但使用快速幂就要快速很多。其计算原理如下(下面底数用a表示,指数用n表示,下面的tmp初始为a,ans初始为1):
n=19的二进制是10011,
此时n为奇数,则ans=ans*tmp;tmp=tmp*tmp;n右移一位;(ans=1*5,tmp=5*5=25)
此时n为奇数,则ans=ans*tmp;tmp=tmp*tmp;n右移一位;(ans=5*25,tmp=25*25)
此时n为偶数,则直接tmp=tmp*tmp;n右移一位;(ans=125,tmp=625*625)
此时n为偶数,则直接tmp=tmp*tmp;n右移一位;(ans=125,tmp=390625*390625)
此时n为奇数,则ans=ans*tmp;tmp=tmp*tmp;n右移一位;(ans=125*390625*390625,tmp=(390625*390625)^2);
这样就已经求解成功,仅仅用了5次,大大节省了时间。其时间复杂度为O(logn)。
矩阵快速幂,顾名思义,就是矩阵做幂运算。其实际和快速幂差不多,只是需要对矩阵的乘法运算去做*运算符的重载。其中有一个重要的矩阵就是单位矩阵,他就相当于数字1一样,只是它是一个矩阵。例如2x2的单位矩阵为:
a【0】【0】=1;a【0】【1】=0;
a【1】【0】=0;a【1】【1】=1;
#include
using namespace std;
typedef long long ll;
const ll MOD=1000007;
ll quickPow(ll a,ll n){ //求a^n
ll tmp=a;
ll ans=1;
while(n){
if(n&1){
ans=ans*tmp;
}
tmp=tmp*tmp;
n>>=1;
}
return ans;
}
int main(){
//功能需求
//............
return 0;
}
#include
#include
using namespace std;
typedef long long ll;
const int Mod=100000007;
struct maxtri{
ll v[2][2];
maxtri (){
memset(v,0,sizeof(v)); //初始化矩阵
}
maxtri operator *(const maxtri &m){ //重载*运算符
maxtri tmp;
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
for(int k=0;k<2;k++){
tmp.v[i][j]+=(v[i][k]*m.v[k][j])%Mod;
}
}
}
return tmp;
}
};
maxtri M,E,ans; //M即为矩阵,E即为单位矩阵,ans为解
void Init(){
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
if(i==j){
E.v[i][j]=1;
}
}
}
M.v[0][0]=2;M.v[0][1]=1; //具体情况具体值的初始化,这里是举例
M.v[1][0]=1;M.v[1][1]=0;
}
maxtri quickPow(maxtri m,ll n){ //核心代码
maxtri tmp=E;
while(n){
if(n&1){
tmp=tmp*m;
}
m=m*m;
n>>=1;
}
return tmp;
}
int main(){
//功能需求
//............
return 0;
}
主要是求指数幂
a.矩阵相乘;
b.求菲波那切数列的第n项(就是可以从前两个状态推到当前状态,这个关系矩阵的系数还是比较好找的);
c.图论中求对应两个点长度为n的路径数,这是矩阵乘法在图论中的经典应用,其实就是用到了矩阵乘法的特殊性,因为矩阵乘法有三重循环,最里面的一层循环就是在枚举一个点k,i -> k -> j,那么从i -> j的路径数长度为m的条数就等于i-> k路径长度为n的路径数乘上k -> j路径长度为m - n的的路径条数,枚举的过程中全加起来,可以看到,这个过程和矩阵乘法的过程是一样的。矩阵An中的ai,j表示:图中点i到点j经过n条边的路径数。(只要能用矩阵乘法做的事情,一般都要用矩阵快速幂来假设)