快速幂(Exponentiation by squaring)或称:平方求幂,是一种高效计算幂函数的算法。
其以O(logN)
的时间复杂度求任意乘方。
本质上,快速幂是分治思想的一种应用。
案例引入
欲求8的6次方
常规算法:
private double pow(double x, int y) {
double result = 1;
for (int i = 0; i < y; i++) {
result *= x;
}
return result;
}
该算法的时间复杂度为O(N)
。
从数学推导
数学公式:
以此作为突破,整理思路。
欲计算 6 次方,则该式简化为:
根据上述理论,可以写出如下方程式:
整理为代码:
private double pow(double x, int y) {
if (y == 0) {
return 1;
} else if (y % 2 == 1) {
return pow(x, y - 1) * x;
} else {
double temp = pow(x, y / 1);
return temp * temp;
}
}
其中,利用到了 二分查找
的思想。
递归虽然简洁,但会产生额外的栈空间开销。将递归改写为递推(循环),来避免对栈空间的大量占用,也就是非递归快速幂。
非递归快速幂
从另一个视角引入快速幂:
已知定理:
任何自然数都可以转化为 2的n次方相乘
。
例:
二进制上从右往左第n位,分别代表2 的 n - 1
次方,而所有的数都可以用二进制表示,所以我们可以根据二进制来优化幂的计算。
也就是说,所有的幂计算都可以看作是底数的2的n次方幂相乘。
例如:
其中 512、256、128、64、32、4、2和1
都是 2 的 n 次方。
将以上理论转化为代码可得:
double pow(double x, int y){
double result = 1;
while (y != 0)
{
if ((y & 1) == 1) {
result *= x;
}
y >>= 1;
x *= x;
}
return result;
}
当y < 0
时,上述代码存在问题。
则修改为:
public double pow(double x, int y) {
double result = 1.0;
if(y < 0) {
x = 1 / x;
y = -y;
}
while(y > 0) {
if((y & 1) == 1) {
result *= x;
}
x *= x;
y >>= 1;
}
return result;
}