zoukankan      html  css  js  c++  java
  • 快速求幂

    快速幂在算指数时是很高效的,他的基本原理是二进制。

    如果要算

    2^5,可以直接2*2*2*2*2

    但是如果要算

    3^999,指数N太大,计算太慢,所以有一种快速的解法。

    @@@@@@@@@@@@@@@@@@@@@@@@

    以3^21为例。

    2^21=(2^16)×(2^4)×(2^1)

    21的二进制可以写成(10101)----------------而10101可以写成1*2^4+0*2^3+1*2^2+0*2^1+1*2^0

    可以明显看出, 每一个香对应着上边的指数。   2^4=16 2^2=4; 2^0=1   其他项为0;

    (具体推理有空再写)

    @@@@@@@@@@@@@@@@@@@@@@@@

    再找一个例子

    2^13

    13=(1101),-----------------1*2^3+1*2^2+1*2^0, 指数分别是8,4,1;

     2^13=2^(8+4+1)

    ///////A^(一个二进制数如101010)=A^(100000)*A^(00000)*A(1000)*A^(000)*A^(10)*A^(0)=A^(2^5)*A^(2^3)*A^(2^1)

    @@@@@@@@@@@@@@@@@@@@@@@@

    所以要计算X^N,现将N变成二进制,然后依次计算二进制的每位的值(如果为二进制位为0不计算)

    @@@@@@@@@@二进制的计算@@@@@@@@@@@@@@

    计算13的二进制,对其除2取余,记录余数。

    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

    代码如下:

     1 int pow_n(int x, int n)
     2 {
     3     int pw = 1;
     4     
     5     while (n > 0) {
     6         if ((n % 2) == 1)
     7             pw *= x;
     8         x *= x;
     9         n /= 2;
    10 
    11     }
    12     return pw;
    13 }

    计算2^13的步骤就是:

    13=(1101)
    n%2, 最低位是1, 所以把这部分累乘到pw中,(pw=2^1)x自乘后变成x^2,n=n/2;
    n 的这一位是0,所以if不成立,不执行累2乘,    x自乘后变成x^4,n=n/2;
    n 的这一位是1,(pw=2^1*2^4),所以累乘到pw中。x自乘后变成x^8,n=n/2;
    n 的这一位是1,(pw=2^1*2^4*2^8)所以累乘到pw中。


    这是快速求幂的思想,把指数n 分解为2的幂次的和,利用自乘计算x 的(2的幂次)次方, 然后根据需要决定是否累乘相应的幂次。

    这样可以把O(n)次乘法缩短到O(logn)次乘法,实现快速计算。

    n=n/2和 (n%2==1)是检查指数n 的某个二进制位是否是1的手段。

    @@@@@@@@@@@@@@@@@@@@@@@@

    快速幂取模

    我们先从简单的例子入手:求a^b % c = ?

     1 int pow_n(int x, int n)
     2 {
     3     int pw = 1;
     4     
     5     while (n > 0) {
     6         if ((n % 2) == 1)
     7             pw *= x;
     8         x *= x;
     9         n /= 2;
    10 
    11     }
    12     return pw;
    13 }

    这个算法的时间复杂度为O(b).这个算法存在着明显的问题,如果a和b过大,很容易就会溢出。

    那么,我们先来看看第一个改进方案:在讲这个方案之前,要先有这样一个公式:a^b%c=(a%c)^b%c.

    所以可以改进算法:

    1 int ans = 1;
    2 a = a % c; 
    3 for(int i = 1;i<=b;i++){
    4     ans = ans * a;
    5     }
    6 ans = ans % c;

    因子取余之后相乘再取余保持余数不变,那么新算得的ans也可以进行取余,所以得出:

    1 int ans = 1;
    2 a = a % c; //加上这一句
    3 for(int i = 1;i<=b;i++){
    4     ans = (ans * a) % c;
    5 }

    算法虽然在复杂度上没有改进,但是已经比第一个好多了。

    求a^bmodc可以写成一下形式

    那么可以继续改进算法:

     1     int ans = 1;
     2     a = a % c;
     3     if(b%2==1)
     4         ans = (ans * a) mod c; //如果是奇数,要多求一步,可以提前算到ans中
     5     k = (a*a) % c; //令K=a^2%c 
     6     //然后计算K^b/2 
     7     for(int i = 1;i<=b/2;i++){
     8         ans = (ans * k) % c;
     9     }
    10     ans = ans % c;

    明显看出,我们由计算a的b次 改成计算k的b/2次,所以我们可以发现这个过程是可以迭代 下去的。当然,对于奇数的情形会多出一项a mod c,所以为了完成迭代,当b是奇数时,我们通过ans = (ans * a) % c;来弥补多出来的这一项,此时剩余的部分就可以进行迭代了。

    1     int ans = 1;
    2     a = a % c;
    3     while(b>0){
    4         if(b % 2 == 1)
    5             ans = (ans * a) % c;
    6         b = b/2;
    7         a = (a * a) % c;
    8 }

    本算法的时间复杂度为O(logb)。

  • 相关阅读:
    jquery load加载页面内ajax返回的div不能响应页面js的问题的解决方案
    JQuery跳出each循环的方法(包含数组遍历)
    fpm 配置详解
    curl模拟Http请求
    git 修改commit信息
    git-ssh 配置和使用
    初始化目录,并且添加到远程仓库
    【PHP代码审计】 那些年我们一起挖掘SQL注入
    【PHP代码审计】 那些年我们一起挖掘SQL注入
    【PHP代码审计】 那些年我们一起挖掘SQL注入
  • 原文地址:https://www.cnblogs.com/hutonm/p/5433149.html
Copyright © 2011-2022 走看看