緣由
今天打google在codejam上辦的apactest,成績還行吧(最後排名540),第二題逗比了,自己邏輯後來理清楚,但是代碼還是原來的想法,wa了兩次才找到了bug。
第二題需要實現一個整數的pow函數,之前只會遞歸的寫法,今天學會了迭代的寫法,回想《劍指offer》裏也有類似的東西,所以整理成這篇博客。
整數的非負整數次冪(不考慮大數)
線性求冪
比如求2的10次冪,可能有人隨手十幾秒就寫完了:
typedef long long LL;
LL pow(int base, int exponent) {
LL ans = 1;
for (int i = 0; i < exponent; ++i) {
ans *= base;
}
return ans;
}
如果exponent是比較大的呢?有沒有更快的?
快速冪算法登場
我們把exponent表示成二進制的形式,比如15=1111,那麼其實有:
這樣計算的話,我們只需要計算
那麼代碼怎麼寫呢?
typedef long long LL;
LL pow(int base, int exponent) {
LL ans = 1, last_pow = base;
while (exponent > 0) {
// 如果最低位爲1
if ((exponent & 1) != 0)
ans = ans * last_pow ;
exponent = exponent >> 1;
last_pow = last_pow * last_pow;
}
return ans;
}
其實我覺得代碼已經很清楚了,如果還不清楚的話,可以稍微解釋一下(請先自行熟悉位運算):
假如我們使用上面的函數來迭代計算
第一步:發現1011的最低位爲1,ans乘上
第二步:發現101的最低位爲1,ans乘以
第三步,發現10的最低位不爲1,ans不變,10右移一位變成1;
第四步,發現1的最低位爲1,ans乘以
注意每一步裏的
整數次冪
注意現在的問題變成了整數次冪了,也就是包括負數次冪了!
首先回顧一下冪運算的定義:
也就是說,對於負數次冪,其實只要計算它的絕對值次冪,再用1去除就可以了,還要注意檢查計算0的0次冪這種非法情況!
typedef double NumberType;
// 計算非負整數次冪的函數
NumberType powWithoutNegativeExp(NumberType base, int exponent) {
NumberType ans = 1, last_pow = base;
while (exponent > 0) {
if ((exponent & 1) != 0)
ans = ans * last_pow ;
exponent = exponent >> 1;
last_pow = last_pow * last_pow;
}
return ans;
}
// 浮點數相等的判斷比較特別
bool equalD(NumberType numA, NumberType numB) {
static const NumberType ERROR = 1e-12;
return (numA - numB <= ERROR && numA - numB >= -ERROR);
}
// 處理各種情況的冪運算的函數
NumberType pow(NumberType base, int exponent) {
// 0的0次冪沒有意義,拋出異常
if (exponent == 0 && equalD(base, 0)) {
throw logic_error("Zero's zero exponent is undefine.");
}
bool isNegative = false;
if (exponent < 0) {
isNegative = true;
exponent = -exponent;
}
NumberType result = powWithoutNegativeExp(base, exponent);
return isNegative ? 1 / result : result;
}
註釋代碼裏有了~應該是很清晰的。
另附上完整的使用示例:
#include <iostream>
#include <stdexcept>
using namespace std;
typedef double NumberType;
NumberType powWithoutNegativeExp(NumberType base, int exponent) {
NumberType ans = 1, last_pow = base;
while (exponent > 0) {
if ((exponent & 1) != 0)
ans = ans * last_pow ;
exponent = exponent >> 1;
last_pow = last_pow * last_pow;
}
return ans;
}
bool equalD(NumberType numA, NumberType numB) {
static const NumberType ERROR = 1e-12;
return (numA - numB <= ERROR && numA - numB >= -ERROR);
}
NumberType pow(NumberType base, int exponent) {
if (exponent == 0 && equalD(base, 0)) {
throw logic_error("Zero's zero exponent is undefine.");
}
bool isNegative = false;
if (exponent < 0) {
isNegative = true;
exponent = -exponent;
}
NumberType result = powWithoutNegativeExp(base, exponent);
return isNegative ? 1 / result : result;
}
int main() {
for (double i = 1.1; i < 1.5; i+=0.1) {
for (int j = 1; j < 10; ++j)
cout << i << ' ' << j << ' ' << pow(i, j) << endl;
}
return 0;
}