分治算法-快速幂算法

问题描述

如何在较快的时间内计算 x n x^n xn,其中 n n n可以是正数或负数,或0

  • 暴力计算时间复杂度很高!
  • 考虑采用快速幂算法求解。

快速幂算法(Divide and conquer )

  • 只考虑 n n n为正数的情况
  • 对于负数的情况只需要先求出正数的情况,再求倒数
  • 对于 x n x^n xn的情形,考虑划分如下子问题
    • x n x^n xn对半剖分,即分成解答 x ⌊ n / 2 ⌋ x^{\lfloor n/2 \rfloor} xn/2 x n − ⌊ n / 2 ⌋ x^{n - \lfloor n/2 \rfloor} xnn/2
    • 分别求解两个部分。
    • 当n是偶数时,有 ⌊ n / 2 ⌋ = n − ⌊ n / 2 ⌋ \lfloor n/2 \rfloor = n - \lfloor n/2 \rfloor n/2=nn/2
    • 当n是奇数时,有 ⌊ n / 2 ⌋ + 1 = n − ⌊ n / 2 ⌋ \lfloor n/2 \rfloor +1= n - \lfloor n/2 \rfloor n/2+1=nn/2
  • 因此给出了一种递归算法:
    • 递归求解 x ⌊ n / 2 ⌋ x^{\lfloor n/2 \rfloor} xn/2
    • 当n是偶数时直接将结果平方;
    • 当n是奇数时,将上述结果求平方后再乘以x
  • 每次求解子问题时将规模缩小了一半,因此复杂度为(logn)
class Solution {
    public double myPow(double x, int n) {
        double ans = quickpower(x,n);
        return n<0?1/ans:ans;
    }
    public double quickpower(double x,long n)
    {
        if(n == 0)
            return 1;
        double ans = quickpower(x,n/2);
        return n%2==0?ans*ans:ans*ans*x;
    }
}

迭代形式

思路

  • 上述算法还需要递归的栈的空间开销
  • 但是迭代算法的栈的空间开销为 O ( 1 ) O(1) O(1)
  • 还是只在n>0的前提下讨论

考虑一下计算 x 77 x^{77} x77的递归树,可以看到只有当结果乘以 x x x时,得到的结果才对最终结果有贡献。
在这里插入图片描述

  • 如图所示,quickpow(x,0)的结果两两相乘,再乘以x,作为quickpow(x,1)的结果;quickpow(x,1)的结果再两两相乘,不乘以x作为quickpow(x,2)的结果;递归过程如此进行。
  • 由于上述递归过程确实能够返回正确的结果
    • 而注意到在递归计算过程中只在某些地方乘以了结果 x x x
    • 而乘以了 x x x得到的结果将被用作上层递归的全部计算
    • 例如考虑在quickpow(x,4)层对应乘的x值
      • 比如quickpow(x,9)的值可以看作 q u i c k p o w ( x , 4 ) 2 ∗ x quickpow(x,4)^2 * x quickpow(x,4)2x
      • 这个x对最终结果的贡献为 x 8 x^8 x8
    • 显然,如果假定进入函数时为第0层,(例如在图中quick(77))为第0层,往下层数加1.在某层特定递归中乘的 x x x值对于最终结果的贡献为 x 2 n − 1 x^{2^{n-1}} x2n1
    • 一般的,假设在递归的第 k 1 , k 2 ⋯ k m k_1,k_2\cdots k_m k1,k2km层都乘以了x,那么我们的最终结果为: x 2 k 1 − 1 x 2 k 2 − 1 ⋯ x 2 k m − 1 x^{2^{k_1 - 1}} x^{2^{k_2 - 1}} \cdots x^{2^{k_m -1}} x2k11x2k21x2km1如果假定需要求的结果为 x n x^n xn,那么显然具有关系 2 k 1 − 1 + 2 k 2 − 1 + ⋯ + 2 k m − 1 = n 2^{k_1-1} + 2^{k_2-1} + \cdots + 2^{k_m - 1} = n 2k11+2k21++2km1=n
    • 观察上述式子:
      • 显然和n的二进制表示有关系!
      • 对于任何正整数n的二进制表示是唯一的!而将二进制转换为十进制的方法就是将二进制表示中为1的位乘以对应的次幂.即对于上述 k 1 , k 2 ⋯ k m k_1,k_2\cdots k_m k1,k2km来说都是唯一的,就是对应于正整数n的二进制表示中为1的位次序!
      • 所以我们可以唯一的求出 k 1 , k 2 ⋯ k m k_1,k_2\cdots k_m k1,k2km,即在哪一层递归中乘以x
      • 由于给出了哪些步骤需要乘以x,于是可以给出迭代算法,自底向上,按照上述递归树模拟递归算法的进行!

代码

class Solution {
    public double myPow(double x, int n) {
        if(n == 0)
            return 1;
        long n_1 = Math.abs((long)n);
        //获取n的二进制比特位数
        int i;
        double ans = 1;
        for(i = 0;n_1>>i!=0;i++);  //二进制比特位数
        long mask  ;
        if(i == 1)
            mask = 1;
        else mask = (long)(1 << (i - 2)) * 2;
        while(mask!=0)
        {
            if((n_1 & mask)!=0)
            {
                ans = ans * ans * x;
            }
            else ans = ans*ans;
            mask = mask>>1;
        }
        return n<0?1/ans:ans;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值