2529: 信息学奥赛一本通T1632-NOIP2012-同余方程

数学知识

这里其实就是在求数论逆元。数论逆元最常见的求法就是扩展欧几里得算法,实际上就是裴蜀定理。裴蜀定理的代码实现较为简单,这里提供两种编写方式 — 对应两种理解角度。
既然说到扩展欧几里得算法,自然跟欧几里得算法息息相关。欧几里得算法又称辗转相除法。接下来,我们看看如何理解。

角度 1 :系数

由裴蜀定理可知现在,要求解的方程是 ax + by = 1,该式子等价于 ax % b = 1 % b,即代求方程。不做解释,就是最简单的变换 — 就是没啥好解释的。
由求解gcd的过程。或者说,欧几里得算法告诉我们以下方程式等价的,ax + by = bx’ + (a%b)y’ = 1。此时,需要找出 (x,y) 与 (x’,y’) 的关系。因为使用递归编写,所以x’,y’是已知的,现在通过x’,y’求得x,y。不妨令 y’ = x,那么 bx’ - (a//b)bx = by => x’ - (a//b)x = y。

  1. /*
  2. 以下代码使用 C++ 编写,C语言不存在软件级引用
  3. */
  4. int exgcd(int a, int b, int& x, int&y) {
  5. int d = a;
  6. if (b != 0) {
  7. d = exgcd(b, a % b, y, x); // 求解出了 x', y' = x
  8. y -= a/b * x;
  9. }
  10. else {
  11. x = 1, y = 0;
  12. }
  13. return d;
  14. }

角度 2: 等式

考虑到裴蜀定理说,a,b可以表示所有gcd(a,b)的倍数。所以a,b一定可以使用等式表达如:a + 0b = a; 0a + b = b;
而gcd的过程就转嫁到了等式右边单独进行,变换过程就成小学数学了。

  1. int inverse(int number, int modulus) {
  2. int x1 = 1, y1 = 0;
  3. int x2 = 0, y2 = 1;
  4. int next_row = number % modulus;
  5. int times = number / modulus;
  6. int temp;
  7. while (next_row) {
  8. temp = x2;
  9. x2 = x1 - times * x2;
  10. x1 = temp;
  11. temp = y2;
  12. y2 = y1 - times * y2;
  13. y1 = temp;
  14. number = modulus;
  15. modulus = next_row;
  16. next_row = number % modulus;
  17. times = number / modulus;
  18. }
  19. return x2;
  20. }

代码实现

  1. #include<iostream>
  2. #include<vector>
  3. #include<queue>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include <iomanip>
  7. #include <unordered_map>
  8. #include <time.h>
  9. inline void swap(int& num1, int& num2) {
  10. int temp = num1;
  11. num1 = num2;
  12. num2 = temp;
  13. return;
  14. }
  15. int inverse(int number, int modulus) {
  16. int x1 = 1, y1 = 0;
  17. int x2 = 0, y2 = 1;
  18. int next_row = number % modulus;
  19. int times = number / modulus;
  20. int temp;
  21. while (next_row) {
  22. temp = x2;
  23. x2 = x1 - times * x2;
  24. x1 = temp;
  25. temp = y2;
  26. y2 = y1 - times * y2;
  27. y1 = temp;
  28. number = modulus;
  29. modulus = next_row;
  30. next_row = number % modulus;
  31. times = number / modulus;
  32. }
  33. return x2;
  34. }
  35. int main() {
  36. int a, b;
  37. scanf("%d%d", &a, &b);
  38. int ans = inverse(a, b); // 求数论逆元
  39. ans = (ans + b) % b; // 保证是正数解
  40. printf("%d\n", ans);
  41. return 0;
  42. }
点赞(0)
 

0 分

0 人评分

 

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

评论列表 共有 0 条评论

暂无评论