快速幂--斐波那契数列

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     }

 

posted @ 2016-07-30 12:03  lz3018  阅读(660)  评论(0编辑  收藏  举报