【筆記】Binary Extended Euclidean Algorithm
阿新 • • 發佈:2018-11-10
擴充套件歐幾里得演算法
給定非負整數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;
}