Самое быстрое модульное возведение в степень в JavaScript
моя задача-вычислить (g^x) mod p
быстро в JavaScript, где ^
- это возведение в степень, mod
- операция по модулю. Все входные данные являются неотрицательными целыми числами,x
около 256 бит, и p
является простым числом 2048 бит, и g
может иметь до 2048 бит.
большинство программ, которые я нашел, которые могут сделать это в JavaScript, похоже, используют библиотеку JavaScript BigInt (http://www.leemon.com/crypto/BigInt.html). Делать сингл возведение в степень такого размера с помощью этой библиотеки занимает около 9 секунд в моем медленном браузере (Firefox 3.0 с SpiderMonkey). Я ищу решение, которое по крайней мере в 10 раз быстрее. Очевидная идея использования квадрата и умножения (возведение в квадрат,http://en.wikipedia.org/wiki/Exponentiation_by_squaring) слишком медленно для 2048-битных чисел: для этого нужно до 4096 умножений.
обновление браузера-не вариант. Использование другого языка программирования это не вариант. Отправка номеров в веб-службу не является опцией.
реализована ли более быстрая альтернатива?
Update: выполнив некоторые дополнительные приготовления (т. е. предварительно вычисляя несколько сотен полномочий), как рекомендовано в статье http://www.ccrwest.org/gordon/fast.pdf упомянутый в ответе outis ниже, можно сделать 2048-битную модульную экспоненциальность, используя не более 354 модульных умножения. (Традиционный квадрат и умножить метод намного медленнее: он использует максимальные 4096 модульных умножений.) Это ускоряет модульную экспоненциальность в 6 раз в Firefox 3.0 и в 4 раза в Google Chrome. Причина, по которой мы не получаем полного ускорения 4096/354, заключается в том, что модульный алгоритм экспоненции Бигинта уже быстрее, чем квадрат и умножение, потому что он использует сокращение Монтгомери (http://en.wikipedia.org/wiki/Montgomery_reduction).
обновление: начиная от Код BigInt, кажется, стоит сделать два уровня ручного оптимизированного (и встроенного) умножения Карацубы (http://en.wikipedia.org/wiki/Karatsuba_algorithm), и только затем вернитесь к базовому-32768 o(n^2) умножению, реализованному в BigInt. Это ускоряет умножение в 2,25 раза для 2048-битных целых чисел. К сожалению, операция по модулю не становится быстрее.
Update: использование измененного сокращения Барретта, определенного в http://www.lirmm.fr/arith18/papers/hasenplaugh-FastModularReduction.pdf и умножение Карацубы и вычислительные мощности (как определено в http://www.ccrwest.org/gordon/fast.pdf), я могу получить время, необходимое для одного умножения с 73 секунд до 12,3 секунд в Firefox 3.0. Кажется, это лучшее, что я могу сделать, но все еще слишком медленно.
Update: интерпретатор ActionScript 2 (AS2) в Flash Player не стоит использовать, потому что он кажется медленнее, чем интерпретатор JavaScript в Firefox 3.0: для Flash Player 9 он кажется в 4,2 раза медленнее, а для Flash Player 10-в 2,35 раза медленнее. Кто-нибудь знает разницу в скорости между ActionScript2 и ActionScript3 (AS3) в число chrunching?
Update: интерпретатор ActionScript 3 (AS3) в Flash Player 9 не стоит использовать, потому что он имеет примерно такую же скорость, как JavaScript int Firefox 3.0.
Обновление: Интерпретатор ActionScript 3 (AS3) в Flash Player 10 может быть до 6,5 раз быстрее, чем интерпретатор JavaScript в Firefox 3.0, Если int
вместо Number
и Vector.<int>
вместо Array
. По крайней мере, это было в 2,41 раза быстрее для 2048-битного большого целочисленного умножения. Поэтому, возможно, стоит сделать модульную экспоненцию в AS3, выполнив ее в Flash Player 10, если она доступна. Обратите внимание, что это все еще медленнее, чем V8, интерпретатор JavaScript Google Chrome. Видеть http://ptspts.blogspot.com/2009/10/javascript-and-actionscript-performance.html Для сравнения скорости различных реализаций языка программирования и JavaScript.
Update: существует очень быстрое решение Java, которое можно вызвать из JavaScript браузера, если установлен плагин Java. Следующее решение примерно в 310 раз быстрее, чем чистая реализация JavaScript с использованием BigInt.
<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#)?
5 ответов
будет ли приемлема какая-либо другая клиентская технология, вызываемая из JS, такая как Java-апплет или Flash-фильм? Типа bigint это подход уже довольно быстро. Вы можете настроить BigInt, или вы можете попробовать другой алгоритм, но вы, вероятно, не получите улучшения на порядок.
Я использую " % "для модульного (mod) и" / " для целочисленного деления. Пусть функция f (p,g,x,r) вычисляет (r*g^x)%p при условии, что r
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? Время будет временем для одной поездки туда и обратно (менее 9 секунд), плюс время для сервера, чтобы вычислить результат, используя некоторую библиотеку BigInt в собственном коде. Это, вероятно, будет намного быстрее.
попробуйте это модульное сокращение Монтгомери от http://code.google.com/p/bi2php/ на JavaScript.