您的位置:首页 > Web前端

《剑指offer》:自己写一个pow函数

2016-08-30 10:58 218 查看

缘由

今天打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,那么其实有:

a11=a10112=a10002+102+12=a10002∗a102∗a12=a8∗a2∗a1

这样计算的话,我们只需要计算log2(exponent)次就够了!对比232和32,完全不一样的数量级!

那么代码怎么写呢?

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;
}


其实我觉得代码已经很清楚了,如果还不清楚的话,可以稍微解释一下(请先自行熟悉位运算):

假如我们使用上面的函数来迭代计算311,根据上面所使用的公式,11表示为二进制是1011,计算过程是这样的:

第一步:发现1011的最低位为1,ans乘上31为3,1011右移一位变成101;

第二步:发现101的最低位为1,ans乘以32变成27,101右移一位变成10;

第三步,发现10的最低位不为1,ans不变,10右移一位变成1;

第四步,发现1的最低位为1,ans乘以38变成177147,1右移一位变成0,退出while循环。

注意每一步里的31,32,34,38是怎么来的呢?用的就是last_pow(注意初始值)这个量来每次平方自己计算出来的!第三步里虽然ans不变,但是last_pow还是得平方一下,否则没法在第四步里变成所需要的38。

整数次幂

注意现在的问题变成了整数次幂了,也就是包括负数次幂了!

首先回顾一下幂运算的定义:

a−x=1ax,x为正整数

a0=1,a不等于0

也就是说,对于负数次幂,其实只要计算它的绝对值次幂,再用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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: