Euclid.cc 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // This is a collection of useful code for solving problems that
  2. // involve modular linear equations. Note that all of the
  3. // algorithms described here work on nonnegative integers.
  4. #include <iostream>
  5. #include <vector>
  6. #include <algorithm>
  7. using namespace std;
  8. typedef vector<int> VI;
  9. typedef pair<int,int> PII;
  10. // return a % b (positive value)
  11. int mod(int a, int b) {
  12. return ((a%b)+b)%b;
  13. }
  14. // computes gcd(a,b)
  15. int gcd(int a, int b) {
  16. int tmp;
  17. while(b){a%=b; tmp=a; a=b; b=tmp;}
  18. return a;
  19. }
  20. // computes lcm(a,b)
  21. int lcm(int a, int b) {
  22. return a/gcd(a,b)*b;
  23. }
  24. // returns d = gcd(a,b); finds x,y such that d = ax + by
  25. int extended_euclid(int a, int b, int &x, int &y) {
  26. int xx = y = 0;
  27. int yy = x = 1;
  28. while (b) {
  29. int q = a/b;
  30. int t = b; b = a%b; a = t;
  31. t = xx; xx = x-q*xx; x = t;
  32. t = yy; yy = y-q*yy; y = t;
  33. }
  34. return a;
  35. }
  36. // finds all solutions to ax = b (mod n)
  37. VI modular_linear_equation_solver(int a, int b, int n) {
  38. int x, y;
  39. VI solutions;
  40. int d = extended_euclid(a, n, x, y);
  41. if (!(b%d)) {
  42. x = mod (x*(b/d), n);
  43. for (int i = 0; i < d; i++)
  44. solutions.push_back(mod(x + i*(n/d), n));
  45. }
  46. return solutions;
  47. }
  48. // computes b such that ab = 1 (mod n), returns -1 on failure
  49. int mod_inverse(int a, int n) {
  50. int x, y;
  51. int d = extended_euclid(a, n, x, y);
  52. if (d > 1) return -1;
  53. return mod(x,n);
  54. }
  55. // Chinese remainder theorem (special case): find z such that
  56. // z % x = a, z % y = b. Here, z is unique modulo M = lcm(x,y).
  57. // Return (z,M). On failure, M = -1.
  58. PII chinese_remainder_theorem(int x, int a, int y, int b) {
  59. int s, t;
  60. int d = extended_euclid(x, y, s, t);
  61. if (a%d != b%d) return make_pair(0, -1);
  62. return make_pair(mod(s*b*x+t*a*y,x*y)/d, x*y/d);
  63. }
  64. // Chinese remainder theorem: find z such that
  65. // z % x[i] = a[i] for all i. Note that the solution is
  66. // unique modulo M = lcm_i (x[i]). Return (z,M). On
  67. // failure, M = -1. Note that we do not require the a[i]'s
  68. // to be relatively prime.
  69. PII chinese_remainder_theorem(const VI &x, const VI &a) {
  70. PII ret = make_pair(a[0], x[0]);
  71. for (int i = 1; i < x.size(); i++) {
  72. ret = chinese_remainder_theorem(ret.second, ret.first, x[i], a[i]);
  73. if (ret.second == -1) break;
  74. }
  75. return ret;
  76. }
  77. // computes x and y such that ax + by = c; on failure, x = y =-1
  78. void linear_diophantine(int a, int b, int c, int &x, int &y) {
  79. int d = gcd(a,b);
  80. if (c%d) {
  81. x = y = -1;
  82. } else {
  83. x = c/d * mod_inverse(a/d, b/d);
  84. y = (c-a*x)/b;
  85. }
  86. }
  87. int main() {
  88. // expected: 2
  89. cout << gcd(14, 30) << endl;
  90. // expected: 2 -2 1
  91. int x, y;
  92. int d = extended_euclid(14, 30, x, y);
  93. cout << d << " " << x << " " << y << endl;
  94. // expected: 95 45
  95. VI sols = modular_linear_equation_solver(14, 30, 100);
  96. for (int i = 0; i < (int) sols.size(); i++) cout << sols[i] << " ";
  97. cout << endl;
  98. // expected: 8
  99. cout << mod_inverse(8, 9) << endl;
  100. // expected: 23 56
  101. // 11 12
  102. int xs[] = {3, 5, 7, 4, 6};
  103. int as[] = {2, 3, 2, 3, 5};
  104. PII ret = chinese_remainder_theorem(VI (xs, xs+3), VI(as, as+3));
  105. cout << ret.first << " " << ret.second << endl;
  106. ret = chinese_remainder_theorem (VI(xs+3, xs+5), VI(as+3, as+5));
  107. cout << ret.first << " " << ret.second << endl;
  108. // expected: 5 -15
  109. linear_diophantine(7, 2, 5, x, y);
  110. cout << x << " " << y << endl;
  111. }