快速幂详解

快速幂

力扣50. Pow(x, n)

力扣LCR 134. Pow(x, n)

题意:

x n x^n xn

  • -100.0 < x < 100.0
  • - 2 31 2^{31} 231 <= n <= 2 31 − 1 2^{31-1} 2311
  • n 是一个整数
  • 要么 x 不为零,要么 n > 0
  • − 1 0 4 -10^4 104 <= x n x^n xn <= 1 0 4 10^4 104

思路一:循环将x乘n次,龟速幂。但是当n很大时,会因迭代次数过多而超时。

思路二:二进制优化。

对于正整数n,可以分解为 n = 2 n 1 + 2 n 2 + . . . + 2 n k n=2^{n_1}+2^{n_2}+...+2^{n_k} n=2n1+2n2+...+2nk。即 x n = x 2 n 1 + 2 n 2 + . . . + 2 n k = x 2 n 1 x 2 n 2 . . . x 2 n k x^n=x^{2^{n_1}+2^{n_2}+...+2^{n_k}}=x^{2^{n_1}}x^{2^{n_2}}...x^{2^{n_k}} xn=x2n1+2n2+...+2nk=x2n1x2n2...x2nk

例如n=5,二进制表示为0101,观察1的位置(分别是第0、2位),所以 5 = 2 0 + 2 2 5=2^0+2^2 5=20+22;若x=3,则 3 5 = 3 2 0 + 2 2 = 3 1 3 4 3^5=3^{2^0+2^2}=3^13^4 35=320+22=3134

对于n<0的情况,转化一下即可: x n → ( 1 x ) − n x^n \rightarrow (\frac{1}{x})^{-n} xn(x1)n。注意当n=-2^31是,其负数是2^31超出了int范围的上界2^31-1,所以代码上要用long类型接收n

这样的话,只要计算 3 1 3 4 3^13^4 3134,而不用计算 3 ∗ 3 ∗ 3 ∗ 3 ∗ 3 3*3*3*3*3 33333。只需要最多32位即可表示int范围内的整数n,所以只需log(n)次运算就可以完成幂的计算。

关键在于如何完成转化: 3 5 = 3 2 0 + 2 2 = 3 1 3 4 3^5=3^{2^0+2^2}=3^13^4 35=320+22=3134。我们可以发现拆分的项数是和二进制1的数量相同的,只需要在每次遇到二进制1时乘对应值即可,而对应的值即是累乘x

3 5 = 3 1 3 4 3^5=3^13^4 35=3134 5 = ( 0101 ) b , a n s = 1 5=(0101)_b,ans=1 5=(0101)bans=1

  1. 对于第0位(1), a n s ans ans乘上此时的对应值 3 2 0 = 3 1 3^{2^0}=3^1 320=31,然后x累乘得第1位的对应值 3 ∗ 3 = 3 2 1 = 3 2 3*3=3^{2^1}=3^2 33=321=32
  2. 对于第1位(0),不需要计算答案,继续累乘x得第2位的对应值 3 2 3 2 = 3 2 2 = 3 4 3^23^2=3^{2^2}=3^4 3232=322=34
  3. 对于第2位(1), a n s ans ans乘上此时的对应值 3 4 3^4 34
  4. 对于第3位(0),不需要计算答案,继续累乘x得第2位的对应值
  5. b的二进制位已遍历结束,最后 a n s = 3 1 3 4 ans=3^13^4 ans=3134

显然遍历b的二进制位的时间复杂度为 l o g ( n ) log(n) log(n),代码如下:

// x^n
class Solution {
public:
    double myPow(double x, int n) {
        double ans = 1;   // 最终答案
        long b = n;		// long接受防止越界
        
        if(b < 0)	// 处理负数幂的情况
            x = 1 / x,b = -b;
        while(b){   // 遍历b的二进制位(移位)
            if(b & 1)  // 二进制位为1
                ans *= x;   // 累乘答案
            x *= x;   // 对应值
            b >>= 1;  // 右移(除2)
        }
        return ans;
    }
};
  • 50
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值