zoukankan      html  css  js  c++  java
  • 幂模运算

    a% m 的值,有这几种方法:

    一、因为 a % m = r 

           所以 a = mk + r 

           同乘b,得a*b = mkb + rb

           则 a* b % m = (mkb + rb) % m = r*b % m = (a % m)*b  % m = b*(a % m) % m,

      即:(a*b) % m = (b*(a % m)) % m

       根据这个公式,得:ab % m = (a * (ab-1 % m)) % m

        然后开始递归:

    function recursionMod(a, b, m) {
        /*
            根据公式: (a*b) % m = (b * (a % m)) % m 递归
            a:5 b:15 m:6 result: 5
         */
        if (b == 1) {
            return a % m
        }
        return (a * arguments.callee(a, b - 1, m)) % m
    }

      迭代的版本:

    function iterationMod(a, b, m) {
        /*
            非递归版本
         */
        var result = 1
        for(var i = 0 ; i < b; i++) {
            result = (a * result) % m
        }
        return result
    }

    二、先证明 (a * b) % m = (a % m) * (b % m) % m

      证明过程:

        设 a = k1 * m + r1       b = k2 * m + r2

                  则: a * b = k1 * m * k2 * m + k1 * m * r2 + k2 * m * r1 + r1 * r2

        则 (a *b) % m = (r1 * r2) % m = (a % m) * (b % m) % m, 得证.

      于是,对于a17 % m,有如下过程:

      a15  %  m = (a % m) * ( a14 % m) % m

           a14 %  m = (a7 % m) * (a7 % m) % m

           a7 % m = (a % m) * (a6 % m) % m

           a6 % m = (a3 % m) * (a3 % m) % m

      a3 % m = (a % m) * (a2 % m) % m

      a2 % m = (a % m) * (a % m) % m

    可以看出,相比方法一,循环次数最多可以降低到幂数b的一半。为什么说是最多,因为有的数要多于幂数b的一半,比如说5:

      a5  %  m = (a % m) * ( a4 % m) % m

           a4 %  m = (a % m) * (a3 % m) % m

           a3 % m = (a % m) * (a2 % m) % m

      a2 % m = (a % m) * (a % m) % m

    循环了四次。

    代码实现如下:

    function halfIterationMod(a, b, m) {
        var result = 1
        while (b>0) {
            if (b % 2 == 0) {
                a = a * a % m
                b = b / 2
            }
            else {
                result = result * a % m
                b = b - 1
            }
        }
        return result
    }

    三、对于a% m,对于任意正整数b,b的二进制表示为:

          b = b* 20 + b* 21 + ......+ bn-1 * 2n-1,n为二进制位数

      所以,ab % m = a(b* 2^0 + b* 2^1 + ......+ bn-1 * 2^(n-1))  %  m

                   = (ab0 * 2^0 % m) *  (ab1 * 2^1 % m) * ..... * (abn-1 * 2^(n-1) %  m) % m

      而对于任意一项:abi * 2^i,都有:

        abi * 2 ^ i  % m = abi * 2 ^ (i-1)  *  abi * 2 ^ (i-1) % m

                    = (abi * 2^(i-1) % m) * (abi * 2^(i-1) % m) % m

                    = ( (abi)2^(i-1) % m ) * ( (abi)2^(i-1) % m ) % m

                       = ( bi * (a2^(i-1) % m) ) *  ( bi * (a2^(i-1) % m) ) % m 

                    = bi * ( a2^(i-1) % m ) * ( a2^(i-1) % m ) % m

        即:abi * 2 ^ i  % m = bi * ( a2^(i-1) % m ) * ( a2^(i-1) % m ) % m

    系数bi的值为0时,abi * 2^i是1,abi * 2^i  % m = 1 不参与最终的计算。

    值为1时,abi * 2 ^ i  % m 的值是前一项的平方,再取模。

    代码实现如下:

    function shiftMod(a, b, m) {
        var result = 1
        var base = awhile (b) {
            if (b & 1) {
                result = result * base % m
            }
            base = base * base % m
            b = b >>> 1
        }
        return result
    }

    测试

    测试代码:

    var a = 23, b = 23, m = 5
    
    var dateNow = new Date()
    var t = recursionMod(a, b, m)
    console.log("结果:", t, " 递归cost:", new Date().getTime() - dateNow.getTime())
    
    dateNow = new Date()
    t = iterationMod(a, b, m)
    console.log("结果:", t, " 全循环迭代cost:", new Date().getTime() - dateNow.getTime())
    
    dateNow = new Date()
    t = halfIterationMod(a, b, m)
    console.log("结果:", t, " 半循环cost:", new Date().getTime() - dateNow.getTime())
    
    dateNow = new Date()
    t = shiftMod(a, b, m)
    console.log("结果:", t, " 蒙哥马利cost:", new Date().getTime() - dateNow.getTime())

    对于,a = 23, b = 23, m = 5,输出:

     a = 23, b = 23, m = 5

    加大a, b 的值:

    var a = 14024, b = 14024, m = 5

     递归报栈溢出错误:

    其他的方法正常。

    继续加大a,b的值: var a = 140242222, b = 140224222, m = 5,此时迭代的耗时剧增:

    再对a,b各增加10倍: var a = 1402422222, b = 1402242222, m = 5,此时等很久:

     再加大:var a = 3453456456534545, b = 3453456456534545, m = 9576

    这两个的结果竟然不一样? 难道是算法出错了么? 或者是数太大了溢出了?

    看看JS里的最大安全整数值是多少:

    是16位的数字

    而a的值位数是:

    也是16位,在上面的代码里都有 a * a 的运算,16位乘16位,结果肯定溢出了。

  • 相关阅读:
    Android 捕获异常并在应用崩溃后重启应用
    Android 浏览器 —— 使用 WebView 实现文件下载
    给 Android 研发的一些的建议
    java.util.concurrent.RejectedExecutionException
    java的关闭钩子(Shutdown Hook)
    PGP工作原理及其安全体制
    漫画:什么是红黑树?
    LINUX下IDEA等工具调试项目时提示:Unable to open debugger port
    MongoDB aggregate 运用篇(转)
    Java8系列之重新认识HashMap
  • 原文地址:https://www.cnblogs.com/cool-fire/p/8423566.html
Copyright © 2011-2022 走看看