1. 程式人生 > >【筆記】Binary Extended Euclidean Algorithm

【筆記】Binary Extended Euclidean Algorithm

擴充套件歐幾里得演算法

給定非負整數a, b,求解向量(u1, u2, u3),使得au1 + bu2 = u3 = gcd(a, b)。

擴充套件歐幾里得演算法的除法版本

引入輔助向量(v1, v2, v3),使得av1 + bv2 = v3 程式碼如下:

/// <summary>
/// 返回{x, y, gcd}, 使得<paramref name="a"/>x + <paramref name="b"/>y = gcd(<paramref name="a"/>, <paramref name="b"/>)
/// </summary>
public static int[] ExtendedEuclidDivision(int a, int b)
{
    if (a < 0 || b < 0)
        throw new ArgumentOutOfRangeException("a and b should all be nonegative");

    if (a == 0) return new int[] { 0, 1, b };
    if (b == 0) return new int[] { 1, 0, a };

    var aZeros = a.NumberOfTrailingZeros();
    var bZeros = b.NumberOfTrailingZeros();
    var k = Math.Min(aZeros, bZeros);
    a >>= k; b >>= k;

    var u = new int[] { 1, 0, a };
    var v = new int[] { 0, 1, b };
    var t = new int[3];
    while(v[2] != 0)
    {
        var q = u[2] / v[2];
        t[0] = u[0] - v[0] * q;
        t[1] = u[1] - v[1] * q;
        t[2] = u[2] - v[2] * q;

        u[0] = v[0]; u[1] = v[1]; u[2] = v[2];

        v[0] = t[0]; v[1] = t[1]; v[2] = t[2];
    }

    u[2] <<= k;

    return u;
}

擴充套件歐幾里得演算法二進位制版本

參考上述除法版本及二進位制版本歐幾里得演算法。引入輔助向量(v1, v2, v3)。使得

av1 + bv2 = v3 

在v3右移1位過程中,當v1和v2同時為偶數時,可以直接移位,否則則需要將其調整為偶數後再移位。

a(v1 + b) + b(v2 - a) = av1 + ab + bv2  - ab = av1 + bv2 = v3

a >>= Math.min(aZeros, bZeros) 和 b >>= Math.min(aZeros, bZeros) 確保a和b至少一個奇數。

當v3為偶數時,不能直接移位(v1, v2)時有如下三種情況

1  若v1,v2全奇時,因為奇+奇=偶,奇*奇=奇,奇*奇 + 奇*奇=偶,所以a, b全奇。

2  若v1奇v2偶時,因為偶+偶=偶,奇*偶=偶,  偶*奇 + 奇*偶=偶,所以a偶b奇。

3  若v1偶v2奇時,因為偶+偶=偶,奇*偶=偶,  奇*偶 + 偶*奇=偶,所以a奇b偶。

這樣在上訴三種情況下v1 + b和v2 - a必定為偶數。

Unsigned版本

/// <summary>
/// 返回{x, y, gcd}, 使得<paramref name="a"/>x + <paramref name="b"/>y = gcd(<paramref name="a"/>, <paramref name="b"/>)
/// </summary>
public static long[] ExtendedEuclid(uint a, uint b)
{
    if (a == 0) return new long[] { 0, 1, b };
    if (b == 0) return new long[] { 1, 0, a };

    var aZeros = a.NumberOfTrailingZeros();
    var bZeros = b.NumberOfTrailingZeros();
    var k = Math.Min(aZeros, bZeros);
    a >>= k; b >>= k;

    var u = new long[] { 1, 0, a };
    var v = new long[] { 0, 1, b };
    //from here on we maintain
    //u[0]a + u[1]b = u[2]
    //v[0]a + v[1]b = v[2]
    while (IsEven(u[2]))
    {
        u[2] >>= 1;
        if (IsEven(u[0]) && IsEven(u[1]))
        {
            u[0] >>= 1; u[1] >>= 1;
        }
        else
        {
            u[0] = (u[0] + b) >> 1; u[1] = (u[1] - a) >> 1;
        }
    }

    while (u[2] != v[2])
    {
        if (IsEven(v[2]))
        {
            v[2] >>= 1;
            //Commentary: note that here, since v[2] is even
            //(i)  if v[0], v[1] are both odd then so are a, b
            //(ii) if v[0] is odd and v[1] even then a must be even, so b is odd
            //(iii)if v[1] is odd and v[0] even then b must be even, so a is odd
            //so for each of (i), (ii) and (iii) v[0] + b and v[1] - a are even
            if (IsEven(v[0]) && IsEven(v[1]))
            {
                v[0] >>= 1; v[1] >>= 1;
            }
            else
            {
                v[0] = (v[0] + b) >> 1; v[1] = (v[1] - a) >> 1;
            }
        }
        else if (v[2] < u[2])
        {
            Swap(ref u[0], ref v[0]);
            Swap(ref u[1], ref v[1]);
            Swap(ref u[2], ref v[2]);
        }
        else
        {
            v[0] -= u[0];
            v[1] -= u[1];
            v[2] -= u[2];
        }
    }

    v[2] <<= k;

    return v;

}




/// <summary>
/// <paramref name="n"/>二進位制尾數0的個數
/// </summary>
public static int NumberOfTrailingZeros(this uint n)
{
    if (n == 0) return 32;

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

    return c;
}



public static void Swap<T>(ref T left, ref T right)
{
    var temp = left;
    left = right;
    right = temp;
}

Signed版本

/// <summary>
/// 返回{x, y, gcd}, 使得<paramref name="a"/>x + <paramref name="b"/>y = gcd(<paramref name="a"/>, <paramref name="b"/>)
/// </summary>
public static long[] ExtendedEuclid(int a, int b)
{
    if (a == 0) return new long[] { 0, 1, b };
    if (b == 0) return new long[] { 1, 0, a };

    var anegtive = a < 0;
    var bnegtive = b < 0;
    if (anegtive) a = -a;
    if (bnegtive) b = -b;

    var aZeros = a.NumberOfTrailingZeros();
    var bZeros = b.NumberOfTrailingZeros();
    var k = Math.Min(aZeros, bZeros);
    a >>= k; b >>= k;

    var u = new long[] { 1, 0, a };
    var v = new long[] { 0, 1, b };
    //from here on we maintain
    //u[0]a + u[1]b = u[2]
    //v[0]a + v[1]b = v[2]
    while (IsEven(u[2]))
    {
        u[2] >>= 1;
        if (IsEven(u[0]) && IsEven(u[1]))
        {
            u[0] >>= 1; u[1] >>= 1;
        }
        else
        {
            u[0] = (u[0] + b) >> 1; u[1] = (u[1] - a) >> 1;
        }
    }

    while (u[2] != v[2])
    {
        if (IsEven(v[2]))
        {
            v[2] >>= 1;
            //Commentary: note that here, since v[2] is even
            //(i)  if v[0], v[1] are both odd then so are a, b
            //(ii) if v[0] is odd and v[1] even then a must be even, so b is odd
            //(iii)if v[1] is odd and v[0] even then b must be even, so a is odd
            //so for each of (i), (ii) and (iii) v[0] + b and v[1] - a are even
            if (IsEven(v[0]) && IsEven(v[1]))
            {
                v[0] >>= 1; v[1] >>= 1;
            }
            else
            {
                v[0] = (v[0] + b) >> 1; v[1] = (v[1] - a) >> 1;
            }
        }
        else if (v[2] < u[2])
        {
            Swap(ref u[0], ref v[0]);
            Swap(ref u[1], ref v[1]);
            Swap(ref u[2], ref v[2]);
        }
        else
        {
            v[0] -= u[0];
            v[1] -= u[1];
            v[2] -= u[2];
        }
    }

    if (anegtive) v[0] = -v[0];
    if (bnegtive) v[1] = -v[1];
    v[2] <<= k;

    return v;
}



/// <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 != 0u) { c -= 16; n = t; }
    t = n << 8; if (t != 0u) { c -= 8; n = t; }
    t = n << 4; if (t != 0u) { c -= 4; n = t; }
    t = n << 2; if (t != 0u) { c -= 2; n = t; }
    t = n << 1; if (t != 0u) { c -= 1; }

    return c;
}