題目:數值的整數次方
實現函數 double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。
算法分析:
我們都知道在C語言庫函數中有一個pow函數可以用來求乘方,本題要求實現類似pow的功能。
1、自以爲很簡單的解法:
由於不需要考慮大數問題,這道題看起來很簡單,可能不少應聘者在看到題目30秒後就能寫出如下的代碼:
public double powerWithExponent(double base,int exponent){
double result = 1.0;
for(int i = 1;i<= exponent;i++){
result = result*base;
}
return result;
}
不過遺憾的是,寫的快不一定就能得到面試官的青睞,因爲面試官會問輸入的指數(exponent)小於1即 是0和負數的時候怎麼辦?上面的代碼完全沒有考慮,只包括了指數爲正數的情況。
2、全面但不夠高效的解法,我們離Offer已經不遠了
我們知道當指數爲負數的時候,可以先對指數求絕對值,然後算出次方的結果之後再取倒數。既然有求倒數,我們很自然的就要想到有沒有可能對0求倒數,如果對0求倒數怎麼辦?當底數base是零且指數是負數的時候,我們不做特殊的處理,就會發現對0求倒數從而導致程序運行出錯。怎麼告訴函數的調用者出現了這種錯誤?在Java中可以拋出異常來解決。
最後需要指出的是,由於0的0次方在數學上沒有意義的,因此無論是輸出0還是1都是可以接收的,但這都需要和麪試官說清楚,表明我們已經考慮到了這個邊界值了。
有了這些相對而言已經全面很多的考慮,我們就可以把最初的代碼修改如下:
public class E11Power {
public 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 double powerWithExponent(double base,int exponent){
double result = 1.0;
for(int i = 1;i<= exponent;i++){
result = result*base;
}
return result;
}
//判斷兩個double型數據,計算機有誤差
private 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{
E11Power test = new E11Power();
System.out.println(test.power(3, -1));
}
}
由於計算機表示小數(包括float和double型小數)都會有誤差,我們不能直接用等號(==)判斷兩個小數是否相等。如果兩個小數的差的絕對值很小,比如小於0.0000001,就可以認爲他們相等。
此時我們考慮得已經很周詳了,已經能夠得到很多面試官的要求了。但是如果我們碰到的面試官是一個在效率上追求完美的人,那麼他有可能提醒我們函數PowerWithExponent還有更快的辦法。
3、全面而高效的解法,確保我們能拿到Offer
如果輸入的指數exponent爲32,我們在函數powerWithExponent的循環中需要做31次乘方。但我們可以換一種思路考慮:我們的目標是求出一個數字的32次方,如果我們已經知道了它的16次方,那麼只要16次放的基礎上再平方一次就可以了。而16次方又是8次方的平方。這樣以此類推,我們求32次方只需要5次乘方:先求平方,在平方的基礎上求4次方,在4次方的基礎上求8次方,在8次方的基礎上求16次方,最後在16此方的基礎上求32次方。
也就是說我們可以利用下面這個公示求a的n次方:
這個公式就是我們前面利用O(logn)時間求斐波那契數列時,討論的公式,這個公式很容易就能用遞歸實現。新的PowerWithExponent代碼如下:
/**************************************************************
* Copyright (c) 2016,
* All rights reserved.
* 版 本 號:v2.0
* 題目描述:數值的整數次方
* 實現函數 double Power(double base,int exponent),求base的exponent次方。不得使用庫函數,同時不需要考慮大數問題。
* 輸入描述:請輸入要計算的數值:
* 8
* 請輸入要計算的次方:
* 3
* 程序輸出: 計算的結果爲:512.0
* 問題分析: 無
* 算法描述:如果輸入的指數exponent爲32,我們在函數powerWithExponent的循環中需要做31次乘方。
* 但我們可以換一種思路考慮:我們的目標是求出一個數字的32次方,如果我們已經知道了它的16次方,
* 那麼只要16次放的基礎上再平方一次就可以了。而16次方又是8次方的平方。這樣以此類推,
* 我們求32次方只需要5次乘方:先求平方,在平方的基礎上求4次方,在4次方的基礎上求8次方,
* 在8次方的基礎上求16次方,最後在16此方的基礎上求32次方。
* 完成日期:2016-10-30
***************************************************************/
package org.marsguo.offerproject11;
import java.util.Scanner;
public class Power {
public static double PowerWithUnsignedExponent(double base,long exponent){
if(exponent == 0)
return 1;
if(exponent == 1)
return base;
double result = PowerWithUnsignedExponent(base,exponent>>1); //利用右移運算來代替除2運算
result *= result;
if(exponent%2 ==1)
result *= base;
return result;
}
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入要計算的數值:");
double base = scanner.nextDouble();
System.out.println("請輸入要計算的次方:");
long exp = scanner.nextLong();
scanner.close();
System.out.println("計算的結果爲:" + PowerWithUnsignedExponent(base, exp));
}
}
程序運行結果: