《剑指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; }
相关文章推荐
- C语言10的n次方pow函数不好用,自己写一个简单的
- 自己写的一个测试函数执行效率的单元(test on Delphi 7)
- 自己写的一个用函数实现的日历
- 六(2).自己动手生产一个getElementsByClass()函数
- 自己写的一个固定表头JS函数
- 写5个不同的自己的函数,来截取一个全路径的文件的扩展名,允许封装php库中已有的函数。
- 自己写的一个数据库自定义函数实例
- sql语句 自己编写一个函数reversion,完成颠倒一个字符串,即:select dbo.reversion('abcd') 输出的结果为 dcba
- BCB平台下,自己写的一个截取字符串的函数
- 一个写js时大家常用的四个公共函数(自己写的,很简单大家都会)
- 用 JavaScript 写 ASP,似乎很爽啊,自己封装了一个 操作数据库 分页 以及 文件操作的函数
- 自己写的一个UBB转换的函数
- 自己写的一个大小写金额转化函数,贴出来大家看看
- 自己编写的一个画圆的小函数
- C#自己编写的一个函数 可以删除字符串中指定开头和结尾中间的字符串
- C#自己编写的一个函数 可以删除字符串中指定开头和结尾中间的字符串
- 金额转大写的函数 (网上找到的几个有Bug 只好自己写了一个)
- [收集]自己编写一个SQL Server中用的lastindexof函数
- 自己整理的一个javascript日期处理函数
- 自己实现的一个字符串分割截取函数,以及查找指定字符