1.基本快速幂
幂运算an即n个a相乘。快速幂就是高效的算出an,当n很大的时候,写题的话基本就超时了。当然基本的快速幂也不只一种方法,下面介绍一种用位运算实现的方法:
首先,就是要把n看成二进制,例如计算a11,实则就是计算a(1101)B,也就是最终可以转化为a(8+2+1)即是a8×a2×a1。如下图
那么显然,用位运算去遍历n的每一位(遍历n的每一位只要用n和1 做与运算,再每一将n右移一次即可),只要是该位为1,那么我们就要乘上一个ak,k肯定就是对应位的权值,例如已经遍历到(1101)B的最高位,发现是1,那么此时这个位置对应的权值是8,所以我们应该再乘上a8。因为是二进制,所以相对高一位的k肯定是低一位的k的两倍,对应的ak肯定就成平方关系,如下图。
而且我们还能知道第一个要乘的肯定就是a,也就是a的20次方,所以最低位要乘的数我们永远都知道,而且又知道高一位又是低一位的平方,所以我们只要定义一个变量,假如变量名为base,初始值为a,只要往高位走了一位就执行base*=base。这样下次可能要乘的值就很容易的求出来了,那么用于保存答案的变量ans是否要乘以base就靠位运算判断了,n对应位置的对应数值为1就乘,反之不乘。最终结束的条件自然是n对应二进制的最高位也已经遍历完。
代码:
int fastpow(int a,int n)
{
int base = a;
int ans = 1;
while(n)
{
if(n & 1)
ans*=base;
base*=base;
n>>=1;
}
return ans;
}
快速幂一般值会很大,一般需要取模:
int fastpow(int a,int n)
{
int base = a;
int ans = 1;
while(n)
{
if(n & 1)
ans = (ans*base)%mod;
base = (base*base)%mod;
n>>=1;
}
return ans;
}
2.矩阵快速幂
前提学习:线性代数矩阵乘法。
给定一个m×m的矩阵A,求它的n次幂,An。矩阵乘法关键是要用定义好矩阵相乘的操作,无论是重载还是写函数,都要有明确的定义。中心思想还是与基本的快速幂是相同的,所以理解基本的快速幂也是十分必要的。有区别的还是一些定义操作:
const int MAXN=2; //阶数
const int mod=1e4;//模
struct matrix//定义矩阵
{
int m[MAXN][MAXN];
matrix(){
memset(m,0,sizeof(m));}
};
matrix multi(matrix a,matrix b)//定义矩阵乘法
{
matrix ans;
for(int i=0;i<MAXN;+i++)
for(int j=0;j<MAXN;++j)
for(int k=0;k<MAXN;++k)
ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j])%mod;
return ans;
}
定义好一系列的操作以后,就可以写快速幂函数了,这和基本的快速幂是很相似的:
matrix fastm(matrix a,int n)
{
matrix ans;
for(int i=0;i<MAXN;++i)//初始化单位矩阵
ans.m[i][i]=1;
while(n)
{
if(n & 1)
ans=multi(ans,a);
a=multi(a,a);
n>>=1;
}
return ans;
}
2.1递推转矩阵
应用矩阵快速幂的难点并不是把这个代码理解(当然理解是必要的),而是如何把一段递推关系转化为矩阵。经典的例子就是斐波那契数列,f(n)=f(n-1)+f(n-2),很显然,用基本的快速幂是无法应用的,此时就可以用矩阵快速幂。如下图:
所以只要构造好转移矩阵,只要用矩阵快速幂算出Tn-1,再用矩阵乘法得出矩阵A(n),答案就是A(n)左上角的元素(一般而言,具体情况视矩阵设计而定)。所以说,这种题目的关键是通过递推关系找出转移的矩阵递推式,然后结合快速幂进行运算。
相应题目:
HDU 3117