модульного умножения больших чисел в C++
У меня три целых числа A, B (менее 10^12) и C (менее 10^15). Я хочу вычислить (A * B) % C. Я знаю это
(A * B) % C = ((A % C) * (B % C)) % C
но говорят, что если A = B = 10^11 тогда выше выражение вызовет переполнение целого числа. Есть ли простое решение для вышеуказанного случая, или я должен использовать быстрые алгоритмы умножения.
Если мне нужно использовать алгоритм быстрого умножения, то какой алгоритм я должен использовать.
EDIT: Я пробовал выше проблемы в C++ (что не вызывает переполнения, не уверен, почему), но не ответ должен быть ноль?
спасибо заранее.
5 ответов
учитывая вашу формулу и следующий вариант:
(A + B) mod C = ((A mod C) + (B mod C)) mod C
вы можете использовать подход "разделяй и властвуй" для разработки алгоритма, который одновременно прост и быстр:
#include <iostream>
long bigMod(long a, long b, long c) {
if (a == 0 || b == 0) {
return 0;
}
if (a == 1) {
return b;
}
if (b == 1) {
return a;
}
// Returns: (a * b/2) mod c
long a2 = bigMod(a, b / 2, c);
// Even factor
if ((b & 1) == 0) {
// [((a * b/2) mod c) + ((a * b/2) mod c)] mod c
return (a2 + a2) % c;
} else {
// Odd exponent
// [(a mod c) + ((a * b/2) mod c) + ((a * b/2) mod c)] mod c
return ((a % c) + (a2 + a2)) % c;
}
}
int main() {
// Use the min(a, b) as the second parameter
// This prints: 27
std::cout << bigMod(64545, 58971, 144) << std::endl;
return 0;
}
что это O(log N)
вы можете решить эту проблему с помощью Шраге это метод. Это позволяет умножить два подпись цифры a
и z
как с определенным модулем m
без генерации промежуточного числа, большего, чем это.
он основан на приближенной факторизации модуля m
,
m = aq + r
то есть
q = [m / a]
и
r = m mod a
здесь []
обозначает целую часть. Если r < q
и 0 < z < m − 1
, тогда как a(z mod q)
и r[z / q]
лежать в диапазоне 0,...,m − 1
и
az mod m = a(z mod q) − r[z / q]
если это отрицательно, добавьте m
.
[этот метод часто используется в линейных конгруэнтных генераторах случайных чисел].
обновление: Исправлена ошибка, когда старший бит a % c
установлен. (наконечник шляпы: Кевин Хоппс)
Если вы ищете простой над быстро, затем вы можете использовать следующее:
typedef unsigned long long u64;
u64 multiplyModulo(u64 a, u64 b, u64 c)
{
u64 result = 0;
a %= c;
b %= c;
while(b) {
if(b & 0x1) {
result += a;
result %= c;
}
b >>= 1;
if(a < c - a) {
a <<= 1;
} else {
a -= (c - a);
}
}
return result;
}
Извините, но алгоритм godel9 приведет к неправильному результату, когда переменная "a"содержит значение с высоким битом. Это происходит потому, что" a
template <typename IntType>
IntType add(IntType a, IntType b, IntType c)
{
assert(c > 0 && 0 <= a && a < c && 0 <= b && b < c);
IntType room = (c - 1) - a;
if (b <= room)
a += b;
else
a = b - room - 1;
return a;
}
template <typename IntType>
IntType mod(IntType a, IntType c)
{
assert(c > 0);
IntType q = a / c; // q may be negative
a -= q * c; // now -c < a && a < c
if (a < 0)
a += c;
return a;
}
template <typename IntType>
IntType multiplyModulo(IntType a, IntType b, IntType c)
{
IntType result = 0;
a = mod(a, c);
b = mod(b, c);
if (b > a)
std::swap(a, b);
while (b)
{
if (b & 0x1)
result = add(result, a, c);
a = add(a, a, c);
b >>= 1;
}
return result;
}
в этом случае A и B 40-разрядные числа, а C-это 50 разрядное число, которое не является проблемой в 64 битном режиме, если у вас есть внутренние или можете написать ассемблерный код, чтобы использовать 64-бит на 64 бит размножаться, что создает 128-битный результат (продукт на самом деле 80 бит), после чего вам делить 128-разрядное делимое на 50 разрядный делитель, чтобы получить 50 битный остаток (по модулю).
в зависимости от процессора, может быть быстрее реализовать деление на 50 бит константы путем умножения на 81 бит (или меньше) константа. Опять же, предполагая 64-битный процессор ,потребуется 4 умножения и некоторые добавления с последующим сдвигом верхних битов 4-кратного продукта для получения частного. Затем для получения 50-битного остатка используется умножение частного числа на 50 бит по модулю и вычитание (из 80-битного произведения).