Я думаю, что уловка заключается в следующем (я собираюсь сделать это в базе 10, потому что это проще, но принцип должен держаться)
Предположим, вы умножаете a*b mod 10000-1
и
a = 1234 = 12 * 100 + 34
b = 5432 = 54 * 100 + 32
сейчас a*b = 12 * 54 * 10000 + 34 * 54 * 100 + 12 * 32 * 100 + 34 * 32
12 * 54 * 10000 = 648 * 10000
34 * 54 * 100 = 1836 * 100
12 * 32 * 100 = 384 * 100
34 * 32 = 1088
Поскольку x * 10000 ≡ x (mod 10000-1)
[1], первое и последнее слагаемые становятся 648 + 1088. Второе и третье слагаемые - вот где «трюк». Обратите внимание:
1836 = 18 * 100 + 36
1836 * 100 ≡ 18 * 10000 + 3600 ≡ 3618 (mod 10000-1).
Это по сути круговое смещение. Дать результаты 648 + 3618 + 8403 + 1088. И также обратите внимание, что во всех случаях умноженные числа <10000 (так как a <100 и b <100), так что это можно вычислить, если вы могли бы только умножить 2-значные числа вместе и добавьте их. </p>
В двоичном коде все будет работать аналогично.
Начните с a и b, оба - 32 бита. Предположим, вы хотите умножить их мод 2 ^ 31 - 1, но у вас есть только 16-битный множитель (давая 32 бита). Алгоритм будет выглядеть примерно так:
a = 0x12345678
b = 0xfedbca98
accumulator = 0
for (x = 0; x < 32; x += 16)
for (y = 0; y < 32; y += 16)
// do the multiplication, 16-bit * 16-bit = 32-bit
temp = ((a >> x) & 0xFFFF) * ((b >> y) & 0xFFFF)
// add the bits to the accumulator, shifting over the right amount
total_bits_shifted = x + y
for (bits = 0; bits < total_bits_shifted + 32; bits += 31)
accumulator += (temp >> (bits - total_bits_shifted)) & 0x7FFFFFFF
// do modulus if it overflows
if (accumulator > 0x7FFFFFFFF)
accumulator = (accumulator >> 31) + (accumulator & 0x7FFFFFFF);
Уже поздно, поэтому аккумуляторная часть этого, вероятно, не будет работать. Я думаю, что в принципе это правильно, хотя. Кто-то не стесняется редактировать это, чтобы сделать это правильно.
Развернутый, это также довольно быстро, это то, что, как я полагаю, использует PRNG.
[1]: x*10000 ≡ x*(9999+1) ≡ 9999*x + x ≡ x (mod 9999)