Javascript中平方乘幂的模不能正常工作?

时间:2018-09-29 17:06:21

标签: javascript algorithm modulo

在过去的几个小时中,我一直在尝试用Javascript实现Exponentiation by squaring。基本上,我一直在尝试使用Fermat Little Theorem来解决模块化乘法逆。看来应该很简单,但我得到的结果不正确:

const MAX = 10**9 + 7
const inv2 = expBySquare(2, MAX-2)
// inv2 should be 500000004 but is 437533240

function expBySquare(x, n) {
  if (n < 0) throw 'error'
  if (n === 0) return 1
  if (n === 1) return x

  if (n % 2 === 0) return expBySquare((x * x) % MAX, n / 2) % MAX
  return (x * expBySquare((x * x) % MAX, (n - 1) / 2)) % MAX
}

我刚刚尝试在C https://onlinegdb.com/H1tsx4TY7中实现该算法,并且该算法没有问题。我知道我应该预料到JS溢出的问题,但是我认为它可以处理数字,因为所有弱点都使用了模。

2 个答案:

答案 0 :(得分:3)

  

我知道我应该预料到JS溢出的问题,但是我认为它可以处理数字,因为所有弱点都使用了模。

JavaScript实际上并没有带有“溢出”的独特“整数”类型;相反,它只是一个“数字”类型,其值是双精度浮点数,并且容易出现舍入误差。因此,即使x * x的值可以表示为64位整数,也可能不能完全表示为JavaScript数字。 JavaScript数字可以精确表示[[((2 53 -1),2 53 -1]]范围内的任何整数-参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER-但您的计算涉及值超出该范围。

要亲自查看您正在冒险超出该范围,可以插入以下内容:

if (x * x > Number.MAX_SAFE_INTEGER)
    throw 'error: ' + x + ' * ' + x + ' is ' + (x * x);

即使294,967,268×294,967,268实际上是8,7005,689,191,383,82 4 ,您也会看到error: 294967268 * 294967268 is 87005689191383820

要解决此问题,您需要使用较小的MAX值(以确保MAX * MAX <= Number.MAX_SAFE_INTEGER),或使用(部分)大整数库对较大的值执行整数运算正在使用。

答案 1 :(得分:1)

JavaScript数字始终使用64位浮点格式。因此,您只能使用52位尾数,这给您有效的53位。您的方案中的中间值超过了中间值,这将引入舍入误差。第一次发生的是279,632,277的平方。应该为78,194,210,340,204,729(57位),但四舍五入为78,194,210,340,204,730