【笔记】Modular Inverse

扩展欧几里得方法可以求解ax + by = gcd(a, b),当gcd(a, b) = 1时,ax + by = 1。

ax = 1 (mod b),  x即a的模逆。

by = 1 (mod a),y即b的模逆。

最简单的模逆运算就是调用扩展欧几里得方法,扩展欧几里得方法见另一篇笔记。

/// <summary>
/// <paramref name="value"/> ^ -1 (mod <paramref name="modulo"/>) if gcd(<paramref name="value"/>, <paramref name="modulo"/>) = 1。others return -1
/// </summary>
public static int ModInverse(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 result = ExtendedEuclid(mod, modulo);
    return result[2] == 1 ? Mod(result[0], modulo) : -1;
}


/// <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>
/// <paramref name="value"/> ^ -1 (mod <paramref name="modulo"/>)
/// if <paramref name="modulo"/> is prime。
/// </summary>
public static int ModInversePrime(int value, int modulo)
{
    if (modulo <= 0)
        throw new ArgumentOutOfRangeException("modulo should be positive");

    var mod = Mod(value, modulo);
    if (mod == 1) return 1;

    int u = mod, v = modulo, r = 1, s = 0;

    while (u != 0)
    {
        while (IsEven(u))
        {
            u >>= 1;
            if (IsEven(r))
                r >>= 1;
            else
                r = Mid(r, modulo);
        }
        while (IsEven(v))
        {
            v >>= 1;
            if (IsEven(s))
                s >>= 1;
            else
                s = Mid(s, modulo);
        }

        if (u >= v)
        {
            u -= v;
            r -= s;
        }
        else
        {
            v -= u;
            s -= r;
        }
    }

    return Mod(s, modulo);
}


        
public static int Mid(int left, int right)
    => ((left ^ right) >> 1) + (left & right);//Dietz's method

当模为2^k时

/// <summary>
/// <paramref name="value"/> ^ -1 (mod <paramref name="modulo"/>)
/// if <paramref name="value"/> is odd and <paramref name="modulo"/> is power of 2。
/// </summary>
public static int ModInversePowerOf2(int value, int modulo)
{
    if (modulo <= 0)
        throw new ArgumentOutOfRangeException("modulo should be positive");
    if (!IsPowerOf2(modulo))
        throw new ArgumentOutOfRangeException("modulo should be power of 2");
    if (IsEven(value))
        throw new ArgumentOutOfRangeException("value should be odd");

    var mod = Mod(value, modulo);
    if (mod == 1) return 1;

    var k = modulo.NumberOfTrailingZeros();
    var bits = new int[k];
    var tmp = 1;
    for(var i = 0; i < k; ++i)
    {
        bits[i] = tmp & 1;
        tmp = (tmp - bits[i] * mod) >> 1;
    }

    var str = new string(bits.Select(num => num == 1 ? '1' : '0').Reverse().ToArray());
    return Convert.ToInt32(str, 2);
}


/// <summary>
/// <paramref name="n"/>二进制尾数0的个数
/// </summary>
public static int NumberOfTrailingZeros(this int n)
{
    if (n == 0) return 32;

    var c = 31;
    var t = n << 16; if (t != 0) { c -= 16; n = t; }
    t = n << 8; if (t != 0) { c -= 8; n = t; }
    t = n << 4; if (t != 0) { c -= 4; n = t; }
    t = n << 2; if (t != 0) { c -= 2; n = t; }
    t = n << 1; if (t != 0) { c -= 1; }

    return c;
}


/// <summary>
/// return true if n is power of 2
/// </summary>
public static bool IsPowerOf2(int n)
{
    var un = math.Abs(n);
    return un < 1 ? false : (un & (un - 1)) == 0;
}


public static bool IsEven(int n) => (n & 1) == 0;

猜你喜欢

转载自blog.csdn.net/xueyan0096/article/details/83781994