冪模運算

a% m 的值,有這幾種方法:算法

1、由於 a % m = r 安全

       因此 a = mk + r 測試

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

       則 a* b % m = (mkb + rb) % m = r*b % m = (a % m)*b  % m = b*(a % m) % m,3d

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

   根據這個公式,得:ab % m = (a * (ab-1 % m)) % mblog

    而後開始遞歸:遞歸

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
}

  迭代的版本:get

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

 

2、先證實 (a * b) % m = (a % m) * (b % m) % mit

  證實過程:

    設 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
}

 

3、對於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位,結果確定溢出了。

相關文章
相關標籤/搜索