zoukankan      html  css  js  c++  java
  • 快速幂--斐波那契数列

    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     }
  • 相关阅读:
    [转]PHP学习入门的一些基础知识
    原来我一直徘徊在选择中
    do while循环学习
    C#装箱
    我的新手学习失败之谈
    C#教程第五课:方法
    转.iPhone开发网站、论坛、博客
    数据库作业Email发送
    安装卸载WINDOWS服务
    SQL SERVER 2008 数据库收缩语句
  • 原文地址:https://www.cnblogs.com/lz3018/p/5720652.html
Copyright © 2011-2022 走看看