秘訣は次のとおりだと思います(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
[1]以降x * 10000 ≡ x (mod 10000-1)
、最初と最後の項は648+1088になります。2番目と3番目の用語は、「トリック」の出番です。次の点に注意してください。
1836 = 18 * 100 + 36
1836 * 100 ≡ 18 * 10000 + 3600 ≡ 3618 (mod 10000-1).
これは本質的に循環シフトです。648 + 3618 + 8403 + 1088の結果を示します。また、すべての場合で、乗算された数値は<10000(a<100およびb<100であるため)であるため、2桁の数値を複数にまとめることができれば計算可能です。 、およびそれらを追加します。
バイナリでは、同様に機能します。
aとbから始めます。どちらも32ビットです。それらをmod2^ 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)