• 扩展欧几里得算法的一种自顶向下实现


    上一次发博客已然是将近四年前,疫情还没开始的时候。
    在这段时间里我暂别了OI,但是兜兜转转还是回到了计算机这一我一直向往的领域。
    去年这个时候也动笔尝试写过一篇博客,但是工程量似乎比我想象中的要大些,自然烂尾了。
    但今天登录的时候意外发现这几年里我的文章零零星星地有人点赞收藏,甚至有人关注我,实在不胜感激。

    请注意,这篇博客里的代码实现有些问题,请不要参考代码

    言归正传,扩展欧几里得算法是在广义欧几里得除法,也就是辗转相除法的过程中得出裴祖定理 s ⋅ a + t ⋅ b = ( a , b ) s·a+t·b=(a,b) sa+tb=(a,b) s s s t t t的方法。
    文中我会使用%表示求余

    为什么辗转相除法可以得到s和t?

    辗转相除法就是反复利用 a = q b + c , q ∈ Z a=qb+c,q\in\mathbb{Z} a=qb+c,qZ时, gcd ⁡ ( a , b ) = gcd ⁡ ( b , c ) \gcd(a,b)=\gcd(b,c) gcd(a,b)=gcd(b,c)这一定理计算 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b)的过程。在实际应用中表现为 a = q b + r a=qb+r a=qb+r其中 q = ⌊ a b ⌋ , r = a % b q=\lfloor\frac{a}{b}\rfloor,r=a\%b q=ba,r=a%b再令 b , r b,r b,r代替 a , b a,b a,b进行运算,即 r i = q i ⋅ r i + 1 + r i + 2 r_{i}=q_i·r_{i+1}+r_{i+2} ri=qiri+1+ri+2
    我们发现 r i + 2 = r i − q i + 2 ⋅ r i + 1 r_{i+2}=r_i-q_{i+2}·r_{i+1} ri+2=riqi+2ri+1,经过多次代换,我们可以将最终的 r n r_n rn q i q_i qi a , b a,b a,b的线性组合表示

    如何得到s和t

    我们探究这个过程的第一步 a = q 1 b + a % b a=q_1b+a\%b a=q1b+a%b
    a % b = a − q 1 b a\%b=a-q_1b a%b=aq1b
    s 1 a + t 1 b = a % b s_1a+t_1b=a\%b s1a+t1b=a%b s 1 = 1 , t 1 = − q 1 s_1=1, t_1=-q_1 s1=1,t1=q1
    第二步则是
    ( b % ( a % b ) ) = b − q 2 ( a % b ) (b\%(a\%b))=b-q_2(a\%b) (b%(a%b))=bq2(a%b)
    代换得
    s 2 = − q 2 s 1 , t 2 = − q 2 t 1 + 1 s_2=-q_2s_1,t_2=-q_2t_1+1 s2=q2s1,t2=q2t1+1
    所以我们得到了递推公式:
    s 1 = 1 , t 1 = − q 1 s_1=1,t_1=-q_1 s1=1,t1=q1
    s i = − q i s i − 1 , t i = − q i t i − 1 + 1 s_i=-q_is_{i-1},t_i=-q_it_{i-1}+1 si=qisi1,ti=qiti1+1

    简略地编写程序如下:

    #include
    struct tri{
    	int g, s, t;
    };
    tri gcd(int a, int b, int s, int t) {
    	if(a % b == 0) return (tri){b, s, t};
    	return gcd(b, a % b, -(a / b) * s, -(a / b) * t + 1);
    }
    int main() {
    	int a, b; scanf("%d%d", &a, &b);
    	if(a < b) b ^=a, a ^= b, b ^= a;//swap a, b
    	tri ans = gcd(b, a % b, 1, -(a / b));
    	printf("gcd of %d and %d is %d, while %d * %d + %d * %d = %d\n",
    		a, b, ans.g,
    		ans.s, a, ans.t, b, ans.s * a + ans.t * b
    	);
    	main();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这个程序的主体部分在 a < b aa<b时会造成错误的结果,推测可能与我 a , b , s , t a,b,s,t a,b,s,t分别对应 b , a % b , s , t b,a\%b,s,t b,a%b,s,t的偷懒传参方式有关。
    这个方法中 s , t s,t s,t的计算是与 a , b a,b a,b的变化同向的,故而适合笔算。

  • 相关阅读:
    白嫖阿里云服务器,速看!数量不多
    vue3 异步组件
    Rockland丨Rockland HCP抗体开发流程
    软件测试缺陷报告---定义,组成,缺陷的生命周期,缺陷跟踪产后处理流程,缺陷跟踪处理流程,缺陷跟踪的目的,缺陷管理工具
    嵌入式分享合集94
    AlphaTensor论文阅读分析
    C++ Qt 学习(文章链接汇总)
    【OpenCV】 透视变换 生活实际场景中的应用
    【微信小程序】事件传参的两种方式
    linux进程间通信
  • 原文地址:https://blog.csdn.net/qq_39973668/article/details/133659219