【日記】Montgomery Modular Inverse
阿新 • • 發佈:2018-11-06
(r, k) = AlmMonInv(a, m) = (a^-1)(2 ^k) (mod m)
/// <summary> /// 返回{x, k};x = <paramref name="value"/>−1 * 2^k (mod <paramref name="modulo"/>) /// if gcd(<paramref name="value"/>, <paramref name="modulo"/>) = 1 and <paramref name="modulo"/> is odd /// </summary> private static uint[] AlmMonInv(uint value, uint modulo) { if (value >= modulo) throw new ArgumentOutOfRangeException("value should be less than modulo"); uint u = modulo, v = value, r = 0, s = 1, k = 0; while (v > 0) { if (IsEven(u)) { u >>= 1; s <<= 1; } else if (IsEven(v)) { v >>= 1; r <<= 1; } else if (u > v) { u = (u - v) >> 1; r += s; s <<= 1; } else if (v >= u) { v = (v - u) >> 1; s += r; r <<= 1; } ++k; } if (r >= modulo) r -= modulo; return new uint[] { modulo - r, k }; }
設 z = AlmMonInv(a, m) = (a^-1)(2^k) (mod m)
因為MonPro(x, y, m, m') = (xy) R ^-1 (mod m) = (xy)(2^32)^-1 (mod m)
所以ModInverse(a, m) = MonPro(z, 2^32-k, m, m') = (a^-1)(2^k)(2^32-k)(2^-32) (mod m) = a^-1 (mod m)
/// <summary> /// <paramref name = "value" />−1 (mod<paramref name="modulo"/>) /// </summary> public static int MonModInverse(int value, int modulo) { if (modulo <= 0) throw new ArgumentOutOfRangeException("modulo should be positive"); if (modulo == 1) return 0; var mod = Mod(value, modulo); if (mod == 1) return 1; var gcd = MonGCD(1U << 31, (uint)modulo); var result = AlmMonInv((uint)mod, (uint)modulo); if (result[1] > 32) { result[0] = MonPro(result[0], 1U, (uint)modulo, gcd[1]); result[1] -= 32; } result[0] = MonPro(result[0], 1U << (32 - (int)result[1]), (uint)modulo, gcd[1]); return (int)result[0]; } /// <summary> /// <paramref name="value"/> (mod <paramref name="modulo"/>) /// </summary> public static int Mod(int value, int modulo) { if (modulo <= 0) throw new ArgumentOutOfRangeException("modulo should be positive"); var remainder = value % modulo; return remainder < 0 ? remainder + modulo : remainder; } /// <summary> /// returns {R, K},R*(2left) - K*right = 1. /// left = 2 ^ n and right is odd /// </summary> public static uint[] MonGCD(uint left, uint right) { var result = new uint[] { 1UL, 0UL }; var s = left; var t = right; while (left > 0) { left >>= 1; result[1] >>= 1; if (IsEven(result[0])) { result[0] >>= 1; } else { result[0] = ((result[0] ^ t) >> 1) + (result[0] & t);//(result[0] + t) / 2 result[1] += s; } } return result; } private static bool IsEven(uint n) => (n & 1) == 0;
注意:a^-1 (mod m) 只有在gcm(a, m) == 1時存在,本方法並沒有檢查a和m是否互素。
MonModPro 參考《Montgomery Moduluar Exponentiation》