面试问题:
在O(log n)时间复杂度内计算x ^ y
有不同的答案,比如“使用印度幂算法”或者
double power(double x, int y)
{
if(y == 0) return 1;
double d = power(x, y/2);
if(y%2 == 0) return d*d;
else return x*d*d;
}
这是正确的答案吗?
有没有更好的编写此问题代码的方法?
面试问题:
在O(log n)时间复杂度内计算x ^ y
有不同的答案,比如“使用印度幂算法”或者
double power(double x, int y)
{
if(y == 0) return 1;
double d = power(x, y/2);
if(y%2 == 0) return d*d;
else return x*d*d;
}
这是正确的答案吗?
有没有更好的编写此问题代码的方法?
对于基本的数学运算和计算复杂度的问题,通常可以在维基百科上快速找到答案。
一般来说,使用平方法或加链指数运算法可以将计算bn所需的乘法操作次数降至Θ(log n)。寻找最小的乘法序列(即指数的最短加法链)是一个困难的问题,目前还没有有效的算法解决(参见子集和问题),但许多相当有效的启发式算法可供选择。
以下是实现方法:
假设你想算2的1024次方,只需要进行11次乘法操作。
具体做法如下:
2*2 = 2^2
2^2 * 2^2 = 2^4
2^4 * 2^4 = 2^8
2^8 * 2^8 = 2^16
....
2^512 * 2^512 = 2^1024
让我们来分析一下:
power
通过递归调用的方式被调用,调用次数等于原始 y
可以被 2 整除的次数(也就是最大的数字 k
,使得 2^k < y
)。这意味着计算次数大约为 k=log_2(2^k)=log_2(y)~=log(y)
,因此这是一个正确的答案。
“更好的方法”的答案取决于什么被视为“更好”。
这里更快:使用按位运算替换 %
可以大大减少开销;还可以用 y>>1
替换 y/2
:
double power(double x, int y) {
if(y == 0) return 1;
double d = power(x, y>>1);
if((y&1) == 0) return d*d;
else return x*d*d;
}
x%2
通常会被优化为x&1
,同样地,x/2
和x>>1
也是如此。微观优化不是一个好主意,除非你正在解决实际阅读反汇编代码时发现的问题。所有这些只是使用了错误的运算符,使优先级规则更加混乱。例如,(y&1 == 0)
不会做你以为它做的事情。 - Potatoswatterif(y&1)
也是可能的(尽管在某些代码风格中不好):D - SOFe>> 1
比/ 2
更明显,甚至具有不同的语义,例如在PHP中,/
执行浮点除法而>> 1
将操作数转换为整数。 - SOFe