# Pow(x, n)(50)
# 题目
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
# 示例
输入: 2.00000
, 10
输出: 1024.00000
输入: 2.10000
, 3
输出: 9.26100
输入: 2.00000
, -2
输出: 0.25000
# 说明
- -100.0 < x < 100.0
- n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
# 算法
# 快速幂算法(递归)
使用分治算法
的思想,例如求x^64
时,我们不需要将x
连乘64次,而是x -> x^2 -> x^4 -> x^8 -> x^16 -> x^32 -> x^64
每次对上次的结果平方,这样计算6次就得到了结果
如果最后要求的是x^77
时,我们可以将其转化为x -> x^2 -> x^4 -> x^9 -> x^19 -> x^38 -> x^77
,我们可以在其中某一项平方后再乘x
,比如x^38 * x^38 * x === x^77
如果我们倒推整个式子,将会更加容易判断是否需要+1
,所以我们递归的时候也从后向前进行递归:
- 递归结束条件是当前的
n
为0
时返回1
- 传入下一轮递归的是幂运算底数
x
和指数/2
,指数/2
作为下一轮判断是否需要再乘x
并控制总迭代次数 - 每一轮返回结果根据当前的
n
能不能被2
整除,决定返回上一轮的值是平方
还是平方乘x
再myPow()
中调用递归函数mul()
的时候,因为n
可能为负,所以我们需要进行正负判断
export const myPow_2 = (x, n) => {
return n >= 0 ? mul(x, n) : 1 / mul(x, -n);
};
const mul = (x, n) => {
if (n === 0) return 1;
let y = mul(x, ~~(n / 2));
return n % 2 === 0 ? y * y : y * y * x;
};
2
3
4
5
6
7
8
# 快速幂算法(迭代)
以pow(2, 77)
为例子,我们按照之前的分析可知x -> x^2 -> x^4 -> x^9 -> x^19 -> x^38 -> x^77
,这个过程中x^4 -> x^9
、x^9 -> x^19
、x^38 -> x^77
这三个过程需要乘以额外的x
,其余的x
都是第一步中的x
不断平方后的结果
- 对于第一个
x
,我们经过了6次平方,这个x
为结果贡献了2^6
个x
- 对于
x^4 -> x^9
中,我们额外乘了一个x
,这个x
在之后的运算中被平方了3次,这个x
为结果贡献了2^3
个x
- 对于
x^9 -> x^19
中,我们额外乘了一个x
,这个x
在之后的运算中被平方了2次,这个x
为结果贡献了2^2
个x
- 对于
x^38 -> x^77
中,我们额外乘了一个x
,这个x
为结果贡献了1
个x
所以我们指数77
就等于2^6 + 2^3 + 2^2 + 2^0
,而这里的运算恰好可以转换为(1001101)2
,即77的二进制表达,每一个加数对应二进制的一个1
所以我们将整数拆分成二进制的表达式,就可得迭代的规律:
x^77 => x^64 * x^8 * x^4 * x^1 => x^(64 + 8 + 4 + 1) => x^(2^6 + 2^3 + 2^2 + 2^0)
所以我们在迭代中不断地平方x
,从右往左得遇到二进制位为1
时,将累计的x_contribute
乘入结果ans
export const myPow = (x, n) => {
return n >= 0 ? mul(x, n) : 1 / mul(x, -n);
};
const mul = (x, n) => {
let ans = 1;
let x_contribute = x;
while (n > 0) {
if (n % 2 === 1) ans *= x_contribute;
x_contribute *= x_contribute;
n = ~~(n / 2);
}
return ans;
};
2
3
4
5
6
7
8
9
10
11
12
13
← 计算质数(204) 有效的正方形(593) →