簡體   English   中英

JavaScript 中最快的模冪運算

[英]Fastest modular exponentiation in JavaScript

我的問題是在 JavaScript 中快速計算(g^x) mod p ,其中^是取冪, mod是模運算。 所有輸入都是非負整數, x大約有 256 位, p是 2048 位的素數, g最多可以有 2048 位。

Most of the software I've found that can do this in JavaScript seems to use the JavaScript BigInt library ( http://www.leemon.com/crypto/BigInt.html ). 在我的慢速瀏覽器(帶有 SpiderMonkey 的 Firefox 3.0)上,用這個庫進行一次這樣大小的冪運算大約需要 9 秒。 我正在尋找至少快 10 倍的解決方案。 對於 2048 位數字來說,使用平方和乘法(乘方求冪, http://en.wikipedia.org/wiki/Exponentiation_by_squaring )的明顯想法太慢了:它最多需要 4096 次乘法。

升級瀏覽器不是一種選擇。 使用另一種編程語言不是一種選擇。 將號碼發送到 web 服務不是一種選擇。

是否有更快的替代方案實施?

更新:按照文章http://www.ccrwest.org/gordon/fast.pdf在下面 outis 的回答中提到的建議,通過做一些額外的准備工作(即預先計算幾百次冪),可以對 2048-位模冪運算最多僅使用 354 次模乘。 (傳統的平方和乘法方法要慢得多:它使用最多 4096 次模乘。)這樣做在 Firefox 3.0 中將模冪運算速度提高了 6 倍,在 Google Chrome 中提高了 4 倍。 我們沒有得到 4096/354 的完全加速的原因是 BigInt 的模指數算法已經比平方和乘法更快,因為它使用了蒙哥馬利減少( http://en.wikipedia.org/wiki/Montgomery_reduction ) .

更新:從 BigInt 的代碼開始,似乎值得做兩級手動優化(和內聯)Karatsuba 乘法( http://en.wikipedia.org/wiki/Karatsuba_algorithm ),然后才恢復到 base-32768 O( n^2) 在 BigInt 中實現的乘法。 這將 2048 位整數的乘法速度提高了 2.25 倍。 不幸的是,模運算並沒有變得更快。

Update: Using the modified Barrett reduction defined in http://www.lirmm.fr/arith18/papers/hasenplaugh-FastModularReduction.pdf and Karatsuba multiplication and precomputing powers (as defined in http://www.ccrwest.org/gordon/ fast.pdf ),我可以在 Firefox 3.0 中將單次乘法所需的時間從 73 秒縮短到 12.3 秒。 這似乎是我能做的最好的,但它仍然太慢。

Update: The ActionScript 2 (AS2) interpreter in the Flash Player isn't worth using, because it seems to be slower than the JavaScript interpreter in Firefox 3.0: for Flash Player 9, it seems to be 4.2 times slower, and for Flash Player 10,它似乎慢了 2.35 倍。 有人知道 ActionScript2 和 ActionScript3 (AS3) 在數字處理方面的速度差異嗎?

Update: The ActionScript 3 (AS3) interpreter in Flash Player 9 isn't worth using because it has just about the same speed as the JavaScript int Firefox 3.0.

Update: The ActionScript 3 (AS3) interpreter in Flash Player 10 can be up to 6.5 times faster than the JavaScript interpreter in Firefox 3.0 if int is used instead of Number , and Vector.<int> is used instead of Array . 2048 位大 integer 乘法至少快 2.41 倍。 因此,可能值得在 AS3 中進行模冪運算,如果可用,在 Flash 播放器 10 中執行它。 請注意,這仍然比 V8 慢,即 Google Chrome 的 JavaScript 解釋器。 請參閱http://ptspts.blogspot.com/2009/10/javascript-and-actionscript-performance.html以了解各種編程語言和 Z686155AF75A60A0F6E9D80C17F 實現的速度比較

更新:有一個非常快速的 Java 解決方案,如果安裝了 Java 插件,可以從瀏覽器的 JavaScript 調用。 以下解決方案比使用 BigInt 的純 JavaScript 實現快約 310 倍。

<body>hi0
<script type="text/javascript">
document.body.innerHTML += '<br>hi1';
if ('object'==typeof java) {
  var x = new java.math.BigInteger("123456789123456789", 10);
  var p = new java.math.BigInteger("234567891234567891", 10);
  var g = new java.math.BigInteger("3", 10);
  var v = x.modPow(x, p);
  document.body.innerHTML += '<br>' + v.toString();
  document.body.innerHTML += '<br>' + v.toString(16);
} else {
  document.body.innerHTML += '<br>java plugin not installed';
}
</script></body>

任何人都可以將此代碼翻譯為 Silverlight (C#) 嗎?

可以接受一些可以從JS調用的客戶端技術,例如Java applet或Flash電影嗎? BigInt的方法已經相當快了。 您可以調整BigInt,或者您可以嘗試不同的算法 ,但您可能不會獲得一個數量級的改進。

我使用“%”表示模塊(mod),“/”表示整數除法。 設函數f(p,g,x,r)在r <p且g <p的條件下計算(r * g ^ x)%p。 f()可以實現為:

bigint_t f(p,g,x,r) {
  bigint_t i, z = g, y;
  for (i = 1; i < x; ++i) {
    y = z; z *= g;
    if (z > p) break;
  }
  if (i >= x - 1) return r*z%p; // g^x*r%p = g^x*r
  else return f(p,y,x/i,g^(x%i)*r%p); // reduce to (r*g^(x%i)%p)*(g^i)^(x/i)%p
}

此例程涉及更多計算,但每個整數小於4096位,通常遠小於g ^ x。 我相信這可能比直接計算更有效。 還要注意,g ^(x%i)可以以更快的方式計算,因為我們已經計算了g ^(i + 1)。

編輯:看這篇文章 Mehrdad提供了正確(和更好)的解決方案。

為什么不使用像C這樣的更合適的語言在某種Web服務中使用服務器端呢? 然后,時間將是一次往返(少於9秒)的時間,加上服務器使用本機代碼中的一些BigInt庫計算結果的時間。 這可能會快得多。

嘗試使用JavaScript上的http://code.google.com/p/bi2php/進行蒙哥馬利模塊縮減。

我很想看到你修改過的BigInt庫的源代碼 - 它可以在任何地方使用嗎?

最初陳述的問題不再適用,但即使在今天,對於搜索模冪算法的 JavaScript 程序員來說,這個線程仍然漂浮在谷歌的頂部。 希望這可以幫助一些人:

function bn_powMod(a, e, m) {
  // h/t https://umaranis.com/2018/07/12/calculate-modular-exponentiation-powermod-in-javascript-ap-n/
  if (m === 1n)
    return 0n;
  if (e < 0n)
    return bn_powMod(bn_modInv(a, m), -e, m);
  let b = 1n;
  a = a % m;
  while (e > 0n) {
    if (e % 2n === 1n)
      b = (b * a) % m;
    e = e >> 1n;
    a = (a * a) % m;
  }
  return b;
}

function bn_modInv(a, m) {
  // h/t https://github.com/python/cpython/blob/v3.8.0/Objects/longobject.c#L4184
  let b = 1n, c = 0n, m0 = m, q, r;
  while (m) {
    q = a / m;
    r = a % m;
    [a, b, c, m] = [m, c, b - q*c, r];
  }
  if (a !== 1n)
    throw new RangeError("Not invertible");
  if (b < 0n)
    b += m0;
  return b;
}

如果您正在尋找真正高效的解決方案,您可以嘗試Pomcor 的pjclMontExpnpm 的 modules modular-power 前者至少不需要ES6 BigInts,所以它甚至可能是舊瀏覽器的候選者。


如果您來這里是因為您正在使用自己的瀏覽器內基於 RSA 的加密技術,請記住存在定時攻擊無數種實現攻擊,並考慮使用更預先打包的 .

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM