модульного умножения больших чисел в 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-битного произведения).