• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
夏弈
沿着河边的白沙滩行走,希望能找到桃花源。
博客园    首页    新随笔    联系   管理    订阅  订阅
【夏弈的题解记录】快速幂加速乘方

前言

  在开发中,计算乘方,我们一般是使用 Math.pow()->double 方法来进行计算,这个方法最终调用的是一个本地方法 StrictMath.pow->double (Java)。

  那如果要我们自己进行计算呢?原理非常简单,a的n次方就是n个a相乘,当n小于0的时候,就是先计算乘方 -n 的结果,再将结果作为除数去除1即可:

 1 class Solution {
 2     public double myPow(double x, int n) {
 3         if( n==0 || x==1.0){ return 1.0; }
 4         if(n<0){ return 1/(x*myPow(x,(n+1)*-1)); } // 针对如Integer.MIN_VALUE的情况,避免因为正负转换导致的越界问题
 5         double res=x;
 6         while(n-->1){
 7             res*=x;
 8         }
 9         return res;
10      }
11  }

 

  但如果就这么实现,当n很大时时候(比如Integer.MAX_VALUE),求解的过程就要进行n-1次乘法,那整个计算过程消耗的时间就非常可观了,如果在力扣中,还会出现下面的问题:

  

  这个时候,我们就可以利用 快速幂 来实现乘方的加速,求解 a的n次方 时,使用快速幂,可以将算法的时间复杂度降低到 O(log2n)。

 

1. 示例题目描述

  原题:力扣 剑指 Offer 16. 数值的整数次方

  

 

2. 原理描述

  当我们计算 x的n次方时,针对指数n,假设n对应的二进制数为:bm bm-1 bm-2 ... ... b2 b1,根据二进制转十进制的算法,可以得到:

    n = 1*b1 + 2*b2 + 4*b3 + ... ... + 2^m-2*bm-1 + 2^m-1*bm

  从而有

    x^n =  x^( 1*b1 + 2*b2 + 4*b3 + ... ... + 2^m-2*bm-1 + 2^m-1*bm )

      = x^1*b1     *     x^2*b2     *     x^4*b3     *     ... ...      *     x^2^m-2*bm-1     *     x^ 2^m-1*bm 

  这样,我们就可以把求 x^n 的计算过程从 n-1 次乘法运算 降低到了 log2n次乘法运算。因为 bm 是 指数n 二进制形式下的一个数字,所以 bm 的值只有两种情况:1 或者 0。

  1)当 bm=1 的时候,x^ 2^m-1*bm = x^ 2^m-1;

  2)当 bm=0 的时候,x^ 2^m-1*bm = x^0 = 1。

  但我们可以注意到的是,x^ 2^m-1*bm中的 2^m-1 部分只和 bm 在指数n二进制形式下的位置 m 有关,而与 bm 的值无关。

  我们可以从 b1 开始运算,而b1也就对应着 指数n 二进制形式下的第1位(最右一位)。执行完这次乘法后,对n执行一次 右移 >> 运算,使得 b1 被舍弃掉,b2来到第1位,而此时x^ 2^m-1*bm 中的 2^m-1 部分(在这里,我们将这部分称之为 k )也要随着乘法乘一次2,从而由 x^1*b1 中的 k=1(2^0) 变为 k=2(2^1),这使得我们在进行第二次乘法时,乘数就变成了:

  x^k*b2 = x^2*b2 

  重复这个过程,直到 n==0 , 这意味着我们已经完成了整个运算过程,现在的计算结果就是乘方的结果。  

 

3. 实现

  每次乘法中,首先要判断 当前位的 bm 是1还是0,我们通过 n&1 计算来进行判断,只有当第一位为 1 的时候,n&1 的计算结果才是1。我们用 res 记录每次乘法的结果,当 bm=0 的时候,本轮乘法中 res只需要乘 1 即可(可省略),否则就要乘上 x^ 2^m-1*bm  。但无论bm是不是0,作为 x^ 2^m-1*bm  中 2^m-1 的部分,x都要进行一次自乘。然后我们将 n 右移一位。重复这个过程,而跳出循环的条件就是 n==0。

  实现可以参考下面给出的代码(Java):

 1 class Solution {
 2     public double myPow(double x, int n) {
 3         if( n==0 || x==1.0){ return 1.0; }
 4         // 指数为负时的处理
 5         // 为了应对 Integer.MIN_VALUE 的情况,所以将指数加1,避免越界
 6         if(n<0){ return 1/(x*myPow(x,(n+1)*-1)); }
 7         double res=1;
 8         // 快速幂加速乘法,可以指数降低乘法次数
 9         while(n>0){
10             if((n&1)==1){
11                 res*=x;
12             }
13             x*=x;
14             n>>=1;
15         }
16         return res;
17     }
18 }

 

posted on 2020-08-19 18:01  夏弈  阅读(190)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3