剑指Offer(Java版):数值的整数次方

题目:实现函数double Power(double base,int exponent),求base的exponent次方。不得使用库函数,同时不须要考虑大数问题java

一、自觉得很简单的解法:面试

因为不须要考虑大数问题,这道题看起来很简单,可能很多应聘者在看到题目30秒后就能写出以下的代码:app

 

[java] view plain copy函数

 

  1. public double powerWithExponent(double base,int exponent){  
  2.         double result = 1.0;  
  3.         for(int i = 1;i<= exponent;i++){  
  4.             result = result*base;  
  5.         }  
  6.         return result;  
  7.     }  

不错遗憾的是,写的快不必定就能获得面试官的青睐,由于面试官会问输入的指数(exponent)小于1即 是0和负数的时候怎么办?上面的代码彻底没有考虑,只包括了指数为正数的状况。优化

 

二、全面但不够高效的解法,咱们离Offer已经不远了spa

咱们知道当指数为负数的时候,能够先对指数求绝对值,而后算出次方的 结果以后再取倒数。既然有求倒数,咱们很天然的就要想到有没有可能对0求倒数,若是对0求倒数怎么办?当底数base是零且指数是负数的时候,咱们不作特 殊的处理,就会发现对0求倒数从而致使程序运行出错。怎么告诉函数的调用者出现了这种错误?在Java中能够抛出异常来解决。.net

最后须要指出的是,因为0的0次方在数学上没有意义的,所以不管是输出0仍是1都是能够接收的,但这都须要和面试官说清楚,代表咱们已经考虑到了这个边界值了。blog

有了这些相对而言已经全面不少的考虑,咱们就能够把最初的代码修改以下:递归

  1. /** 
  2.  * 题目:实现函数double Power(double base,int exponent),求base的exponent次方。不得使用库函数,同时不须要考虑大数问题 
  3.  * 对于这道题,要考虑四种状况: 
  4.  * 一、底数为0,指数为负数的状况,无心义 
  5.  * 二、指数为0,返回1 
  6.  * 三、指数为负数,返回1.0/base,-exponent 
  7.  * 四、指数正数,base,exponent 
  8.  */

 

package cglib;ip

public class List1
{  
      public static double power(double base,int exponent) throws Exception{  
            double result = 0.0;  
            if(equal(base,0.0) && exponent<0){  
                throw new Exception("0的负数次幂无心义");  
            }  
            if(equal(exponent,0)){  
                return 1.0;  
            }  
            if(exponent <0){  
                result= powerWithExponent(1.0/base, -exponent);  
            }  
            else{  
                result = powerWithExponent(base,exponent);  
            }  
            return result;  
        }  
        private static double powerWithExponent(double base,int exponent){  
            double result = 1.0;  
            for(int i = 1;i<= exponent;i++){  
                result = result*base;  
            }  
            return result;  
        }  
        //判断两个double型数据,计算机有偏差  
        private static boolean equal(double num1,double num2){  
            if((num1-num2>-0.0000001) && (num1-num2<0.0000001)){  
                return true;  
            }else{  
                return false;  
            }  
        }  
        public static void main(String[] args) throws Exception{  
            
            System.out.println(power(3, -1));  
        }
}

 

因为计算机表示小数(包括float和double型小数)都会有偏差,咱们不能直接用等号(==)判断两个小数是否相等。若是两个小数的差的绝对值很小,好比小于0.0000001,就能够认为他们相等。

 

此时咱们考虑得已经很周详了,已经可以获得不少面试官的要求了。可是若是咱们碰到的面试官是一个在效率上追求完美的人,那么他有可能提醒咱们函数PowerWithExponent还有更快的办法。

三、全面而高效的解法,确保咱们能拿到Offer

若是输入的指数exponent为32,咱们在函数 powerWithExponent的循环中须要作31次乘方。但咱们能够换一种思路考虑:咱们的目标是求出一个数字的32次方,若是咱们已经知道了它的 16次方,那么只要16次放的基础上再平方一次就能够了。而16次方又是8次方的平方。这样以此类推,咱们求32次方只须要5次乘方:先求平方,在平方的 基础上求4次方,在4次方的基础上求8次方,在8次方的基础上求16次方,最后在16此方的基础上求32次方。

也就是说咱们能够利用下面这个公示求a的n次方:

 

这个公式就是咱们前面利用O(logn)时间求斐波那契数列时,讨论的公式,这个公式很容易就能用递归实现。新的PowerWithExponent代码以下:

  1. private  static  double powerWithExponent2(double base,int exponent){  
  2.         if(exponent == 0)  
  3.             return 1;  
  4.         if(exponent == 1)  
  5.             return base;  
  6.         double result = powerWithExponent2(base,exponent >>1);  
  7.         result *= result;  
  8.         if((exponent&0x1) == 1)  
  9.             result *=base;  
  10.         return result;  
  11.     }  

最后再提醒一个细节:咱们用右移运算代替除2,用位与运算符代替了求余运算符(%)来判断一个数是奇数仍是偶数。位运算的效率比乘除法及求余运算的效率要高不少。既然要优化代码,咱们就把优化作到极致。

最终代码:

package cglib;

public class List1
{  
      public static double power(double base,int exponent) throws Exception{  
            double result = 0.0;  
            if(equal(base,0.0) && exponent<0){  
                throw new Exception("0的负数次幂无心义");  
            }  
            if(equal(exponent,0)){  
                return 1.0;  
            }  
            if(exponent <0){  
                result= powerWithExponent(1.0/base, -exponent);  
            }  
            else{  
                result = powerWithExponent(base,exponent);  
            }  
            return result;  
        }  
        private static double powerWithExponent(double base,int exponent){  
             if(exponent == 0)  
                 return 1;  
             if(exponent == 1)  
                 return base;  
             double result = powerWithExponent(base,exponent >>1);  
             result *= result;  
             if((exponent&0x1) == 1)//0000011&00000001=00000001,10&01=0
                 {
                 System.out.println("奇数次幂");
                 result *=base;
                 }
                   
             return result;   
        }  
        //判断两个double型数据,计算机有偏差  
        private static boolean equal(double num1,double num2){  
            if((num1-num2>-0.0000001) && (num1-num2<0.0000001)){  
                return true;  
            }else{  
                return false;  
            }  
        }  
        public static void main(String[] args) throws Exception{  
            
            System.out.println(power(3, -5));  
        }
}

输出:

奇数次幂 0.004115226337448559

相关文章
相关标签/搜索