【学习随笔】求快速幂( 反复平方法 )问题

学习参考自:https://www.cnblogs.com/wendelhuang/p/3414738.html

                     https://blog.csdn.net/u011590573/article/details/81457797

                     https://blog.csdn.net/wzb56_earl/article/details/7199343

对于快速幂求解问题不是很了解,所以特地学习下,记下自己的理解,当然可能理解有错误,欢迎指出来  (:逃 毕竟我也刚学会

快速幂

快速幂的目的就是做到快速求幂,假设我们要求a^b,按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn)

第一种实现代码是这个样子的:(一般网上的代码)右->左

 int  poww (int a ,  int b )  {
     int  temp_result = 1, temp_base;
     while (b != 0) {//判断是否终止
         if (b & 1 != 0)       //判断二进制最后一位 0 1
            temp_result *= temp_base; 
            temp_base *= temp_base;   //自增时刻准备着  也是之所以称之为反复平方法的缘由。
            b >>= 1; //右移操作
     }
     return temp_result ;
  }

我对于这种快速幂的理解如下: 

求a^b,因为b一定可以被化为二进制数,假设求3^13,那么13可以被化为1101,1101可以拆成(1000+100+【00】+1)

二进制1000=2^3=8,100=2^2=4,1=2^0=1,现在我们可以理解为求3^13=3^1*【3^0】*3^4*3^8,显然只要4步就可以求出结果

其中加快的部分就是在3^4是temp_base所保存的临时值,到了下一个3^8的时候会利用temp_base*=temp_base得到 

temp_base=  a ,a^2,a^4,a^8,a^16   的递增

 对应本题中 [ 1101 ]      1 ( 2^3 )                 1( 2^2 )               0( 2^1 )               1 ( 2^0 )

 temp_base                   a  ^  8                     a  ^  4                 a  ^  2                  a  ^  1

遇到 0 ,temp_result*=temp_base 这不就不做,遇到 1,就做 temp_result*=temp_base 

             注:  这步含义就是二进制中的加法 [ a^ (8+4+1) ]对应实际操作的乘法[ a^8*a^4*a^1 ]

注: a^8=a^(2^3)=a^(2^2)*a^(2^2)  说明b的二进制 100和1000的关系 a^4和a^8

举例a^13说明快速幂求解过程:

初始化temp_result=1,temp_base=a;

Step1:从右向左看1101,利用if(b&1)来判断,因为式子成立说明b的最后一位是1,即做temp_result*=temp_base(数值a)

              然后 temp_base*=temp_base(数值a^2),为第二次执行temp_result做准备。然后利用b>>1 ,将1101变成110

Step2:从右向左看110,if(b&1)判断式子最后一位为0,所以temp_result还是a,temp_base*=temp_base(数值a^4)

             b>>1 ,将110变成11

Step3:从右向左看11,if(b&1)判断式子最后一位为1,temp_result*=temp_base(数值为a^5=a*a^4),temp_base自乘数值变               为a^8, b>>1 ,将11变成1

Step4:从右向左看11,if(b&1)判断式子最后一位为1,temp_result*=temp_base(数值为a^13=a*a^4*a^8),temp_base自乘                值变为a^16, b>>1 ,将1变成

Output:temp_result = a ^ 13   [ 这样操作后发现,就是遍历二进制数是否为01,次数和二进制数位数有关 ]

(temp_base不管值是否为01,,每次都要temp_base*=temp_base,时刻准备下一次被temp_result用到)

第二种实现是《算法导论》的实现,代码是这个样子的:左->右

int poww(int a, int b) {
    int temp_result = 1;
    for (int i = 31; i>=0; i--) {
       temp_result *= temp_result;   //此处就是反复平方法的名字由来
        if (b & 1<<i) 
            temp_result *= a;
    }
    return temp_result;
}

我对这个代码的理解: a^1 ,   a^11 ,  a^110  ,  a^1101   方向从左向右看

首先 int b , 4字节 32位,去除最高位符号位,剩下31位,进行32次操作,噗~= =~肯定不会出现漏位数,But 多了啊 (: 解释不来

算导的方向和我们上面的不一样,它是从左向右看,其中需要理解的一句是   if (b & 1<<i) 这句,其实加个括号优先级可能就会

好理解了  if (b &(1<<i))假设 i =2,用 1101 &(0100)判断此时的第 i 位是 0 还是 1,好了我们知道这句含义来看整段代码。

还是举例子a^13来说:

初始化temp_result=1

其中 1101前面全是0,所有temp_result不管怎么反复平方还是1

红色代表 i 的位置   蓝色代表还没判断的部分 

注:i 在原二进制数中是第 i 位,但是要当最低位看【计算的时候其实它就是最低位】黑色部分和红色部分,红色确实最低

所以才会在 i 位为1时出现 temp_result *= a 只有最低位是2^0,即a(2^0)=a   

蓝色部分的值不管因为它还没有出现,黑色部分的值也不管因为他已经判断结束了

至于反复平方原因就是,1101看的方向是左到右,1到11,二进制已经升了1位,例如:100(4)->1000(8)升了一位,二进制是2倍的差距,但是a^4和a^8是自乘的差距,这就是原因。所以每次 i 移动都代表了升位的产生,所有要反复平方

                                                       < 以上都是我的个人理解可能有错误 >

Step1: 此时二进制 1101,temp_result*=temp_result 自身平方一次,temp_result值为1, if (b & 1<<i)判断为1

               所以temp_result *= a,temp_result值为a( 1*a )

Step2:此时二进制 1101,temp_result*=temp_result 自身平方一次,temp_result值为a^2, if (b & 1<<i)判断为1

              所以temp_result *= a,temp_result值为a^3( a^2*a )

Step3:此时二进制 1101,temp_result*=temp_result 自身平方一次,temp_result值为a^6, if (b & 1<<i)判断为0

              所以temp_result值不变.

Step4:此时二进制 1101,temp_result*=temp_result 自身平方一次,temp_result值为a^12, if (b & 1<<i)判断为1

             所以temp_result *= a,temp_result值为a^13( a^12*a )

反复平方法求幂运算的两个算法的比较:

方法一与方法二的比较:

   方法二更好,因为M = M*C, 中的C是个常量,对与固定的C可以有些优化算法;

  (1)根据C的二进制形式计算M*C 的方法称之为 binary method。

  (2)加速binary算法可以先将C分解(factor),再利用binary method,

称之为(factor and binary method)

反复平方法求幂运算的应用:

       1.模幂运算:M =C^d  mod N

         只需在原算法中的每步运算中,加上mod N 就可以解决;

     该算法可以用于RSA算法中。

     利用Montgomery算法改写算法中求mod运算可以加速上述算法的执行。

代码如下:

LL pow_mod(LL a, LL b, LL MOD){//a的b次方求余MOD
     LL ret = 1;
     while(b){
         if(b & 1) ret = (ret * a) % MOD; //此处加mod
         a = (a * a) % MOD; //此处加mod
         b >>= 1;
     }
     return ret;
 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值