求 ab % 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、对于ab % m,对于任意正整数b,b的二进制表示为:
b = bo * 20 + b1 * 21 + ......+ bn-1 * 2n-1,n为二进制位数
因此,ab % m = a(bo * 2^0 + b1 * 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位,结果确定溢出了。