快速幂算法详解

适用: 值需要多次翻倍的场景。

题目描述:

实现 pow(x, n) ,即计算 x 的 n 次幂函数(即, x n x^n xn)。不得使用库函数,同时不需要考虑大数问题。

题目链接:数值的整数次方

快速幂引出:

当 n 小于 0 时,结果为 x − n x^{-n} xn 因此只需考虑 n 为正数的情况。

一般情况下,我们会直接计算 x ∗ x x * x xx 循环 n - 1 次,但当 n 非常大时,计算会很耗时。

其实,并不需要必须计算 n - 1 次才能得出结果,使用某些方法计算若干次(通常比 n - 1 次要小很多)就能得出答案,这种方法被称为快速幂方法。

x 77 x^{77} x77 为例:

快速幂 + 递归

x 77 x^{77} x77 可以分解为:

x x x -> x 2 x^2 x2 -> x 4 x^4 x4 -> x 8 x^8 x8 -> x 19 x^{19} x19 -> x 38 x^{38} x38 -> x 77 x^{77} x77

乍一看看不出规律,但从右向左看可以发现, x x x 上的指数是右侧一半的下取整。

其中 x x x -> x 2 x^2 x2 x 2 x^2 x2 -> x 4 x^4 x4 x 4 x^4 x4 -> x 8 x^8 x8 x 19 x^{19} x19 -> x 38 x^{38} x38 右侧可以由左侧的平方取得,右侧的指数为偶数

x 8 x^8 x8 -> x 19 x^{19} x19 x 38 x^{38} x38 -> x 77 x^{77} x77,右侧可以由左侧的平方在多乘一个 x x x 获取,右侧的指数为奇数。

综合其上,可以得到一个递推关系式:

当指数 n n n 为奇数时, x n x^n xn = x n / 2 x^{n/2} xn/2 * x n / 2 x^{n/2} xn/2 * x x x

当指数 n n n 为偶数时, x n x^n xn = x n / 2 x^{n/2} xn/2 * x n / 2 x^{n/2} xn/2

当指数 n = = 0 n == 0 n==0 时, x 0 x^0 x0 = 1

// 快速幂应用
public double myPow(double x, int n) {
    long N = n;
    return n >= 0 ? quickMul(x, n) : 1.0 / quickMul(x, n);
}
public double quickMul(x, n){
    if(n == 0){
        return 1;
    }
    // 先算出 n 是一半的值
    double y = quickMul(x, n / 2);
    // 如果 n 是偶数,则
    return n % 2 == 0 ? y * y : y * y * x;
}

快速幂 + 迭代

任何正整数都能用二进制表示,77 = 64 + 8 + 4 + 1,因此可以写成二进制 ( 1001101 ) 2 (1001101)_2 (1001101)2

x 77 x^{77} x77 = x x x * x 4 x^4 x4 * x 8 x^8 x8 * x 64 x^{64} x64

完整的是 x x x x 2 x^2 x2 x 4 x^4 x4 x 8 x^8 x8 x 16 x^{16} x16 x 32 x^{32} x32 x 64 x^{64} x64,可以看出就是在二进制为1的位置选择了。

因此可以迭代指数,同时计算 x ∗ = x x *= x x=x 得到相应的二进制指数中间结果,如果它的二进制末尾是为1,则保留,与存储最终结果的值相乘,直到指数为0。

// 快速幂应用
public double myPow(double x, int n) {
    long N = n;
    return n >= 0 ? quickMul(x, n) : 1.0 / quickMul(x, n);
}
// 迭代法
public double quick(double x, long n){
    double ans = 1.0;
    double res = x;
    while(n != 0){
        if(n % 2 == 1){
            ans *= res;
        }
        res *= res;
        n /= 2;
    }
    return ans;
}

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值