快速幂--斐波那契数列
1)编写程序,求解a^b。其中b是正整数。
方法1.
//一般求幂算法,O(r) public static long power1(int a,int r){ if(r<0) { System.out.println("r must be positive number!"); return -1; } if(r==0){ return 1; } long res=1; for(int i=1;i<=r;++i){ res*=a; } return res; }
这种使用连乘计算幂值的算法,复杂度是O(n)。不过如果将连乘拆分为若干相乘的表达式就可以减少做乘法的次数,自然也能提高算法效率。
方法2:快速幂计算
以a^10为例,一般方法是a^10=a*a*a*a*a*a*a*a*a*a,做9次乘法操作。为了减少乘法操作次数,首先将指数以二进制形式表示,10的二进制形式为1010。就有a^10=a^(0*2^0+1*2^1+0*2^2+1*2^3)=a^(1*2^1+1*2^3)=(a^2)*((a^2)^3)=(a*a)*((a^2)*(a^2)*(a^2)),将乘法操作次数从9次减少为4次,复用了计算结果a^2。
推导公式:ab=ap(n)*2^n+p(n-1)*2^(n-1)+...+p(0)*2^0=ap(n)*2^n*ap(n-1)*2^(n-1)*...ap(i)*2^i...*ap(0)*2^0,其中p(i)是对应的二进制位,如果是0,对应的项在计算的时候可以省略。另外有a^(2^i)=a^(2^(i-1)*2)=a^((2^(i-1))^2)。
快速幂的代码如下:
public static long quickPower(long A,int k){ if(k<0){ System.out.println("enter positive numbers"); return -1l; } if(0==k){ return 1; } long ans=1; while (k!=0){ if(k%2==1){//二进制位是1 ans*=A; } A*=A; k/=2; } return ans; }
时间复杂度是O(log n)。
递归求解幂运算的代码:
//递归幂计算,O(log r) public static long power2(int a,long r){ if(r<0){ System.out.println("enter positive numbers"); return -1l; } if(0==r){ return 1l; } boolean flag=false; if(0l==r%2l){ flag=true; } long val=flag?r/2l:(r-1)/2l; if(flag){//偶数 long cc=power2(a,val); return cc*cc; } else {//奇数 long cc=power2(a,val); return a*cc*cc; } }
2)快速幂取模算法
(a^b)%c=((a%c)^b)%c。快速幂取模算法是RSA算法的核心,其代码实现如下:
public static long quickPower(long A,int k,int m){ if(k<0){ System.out.println("enter positive numbers"); return -1l; } if(0==k){ return 1; } long ans=1; while (k!=0){ if(k%2==1){//二进制位是1 ans=(ans*A)%m; } A=(A*A)%m; k/=2; } return ans; }
3)使用矩阵快速幂求解斐波那契数列(https://www.zhihu.com/question/28062458)。代码如下:
1 public static long fibonacci4(int k){ 2 3 if(k==0){ 4 return 1; 5 } 6 long[][] c={{1,0},{0,1}};//单位矩阵,代替快速幂中的ans=1 7 long[][] a={{1,1},{1,0}};//待求幂的矩阵,对应快速幂中的a 8 while (k!=0){ 9 if(k%2!=0){ 10 c=multiply(c,a); 11 } 12 a=multiply(a,a); 13 k/=2; 14 } 15 return c[0][0]+c[0][1]; 16 17 } 18 19 public static long[][] multiply(long[][] a,long[][] b){ 20 long[][] c=new long[2][2]; 21 c[0][0]=a[0][0]*b[0][0]+a[0][1]*b[1][0]; 22 c[0][1]=a[0][0]*b[0][1]+a[0][1]*b[1][1]; 23 c[1][0]=a[1][0]*b[0][0]+a[1][1]*b[1][0]; 24 c[1][1]=a[1][0]*b[0][1]+a[1][1]*b[1][1]; 25 return c; 26 }
算法时间复杂度是O(log n),虽然包含矩阵乘法,不过,矩阵始终是2*2的方阵,并不会随着输入规模的增大而有所变化。
另外三种方法求解斐波那契数列。
1 //二分递归求斐波那契数列,将大问题需要拆分为两个子问题,然后分别通过递归调用求解,这就是二分递归。 2 public static int fibonacci1(int k){ 3 return k<=1?k:(fibonacci1(k-1)+fibonacci1(k-2)); 4 }//O(2^k),利用斐波那契数列通项公式求得。大量重复计算 5 6 //线性递归。O(n) 7 public static long[] fibonacci2( long k){ 8 if(k<=1){ 9 long[] intPairs=new long[2]; 10 intPairs[0]=k; 11 intPairs[1]=0; 12 return intPairs; 13 } 14 long[] intPairs=fibonacci2(k-1); 15 long temp=intPairs[1]; 16 intPairs[1]=intPairs[0]; 17 intPairs[0]+=temp; 18 return intPairs; 19 } 20 21 //递推求解,O(n) 22 23 public static long fibonacci3(long k){ 24 if(k<=1){ 25 return k; 26 } 27 long preValue1=0; 28 long preValue2=1; 29 long sum=0; 30 for(int i=2;i<=k;++i){ 31 sum=preValue1+preValue2; 32 preValue1=preValue2; 33 preValue2=sum; 34 35 } 36 return sum; 37 }