• 最大公约数(gcd):Euclid算法证明及其它


    在刚进大学学习C语言的时候,用的是谭浩强的那本“经典”教程(之所以加引号是在接触了更多的书籍和经过了高手的指点后,发现它算不上绝对的经典,但是是入门很好的书)。记得好像是在函数那一章的课后习题的第一题让写一个求两个数最大公约数的方法。因为当时是第一次接触C语言不知道这一个经典的题目,所以就直接翻阅答案去了。答案给出的提示就是用欧几里得的“辗转相除法”。当时把那个方法记了下来,只觉得太神奇了,会使用它但是一直没有理解,也不会证明它。

    今年开始读研究生,选了Algorithm,相对于本科学习算法,在研究生阶段感觉很多是在复习这些内容,但也有更多的深入证明和学习。就拿gcd(Greatest Common Divisor)来说,在第0章就讲解了很多"模数"的知识和应用,在第三节课就证明了gcd的正确性。

    因为gcd算法是一个经典的算法,所以在网上已经有很多关于它的证明方法和各种版本的实现方法。在此重述这些内容,只想巩固和积累一下自己所学到的知识,并方便将来的回顾。因为发现很多学过的内容在许久之后就会忘记,然后就要花很长的时间去找答案(比如大一的时候用得很熟练的求极限的各种公式,现在都想不太起来了,以致在解决问题的时候总是在怀疑做得是否正确)。

    1个常识:

    如果 a≥b 并且 b≤a,那么 a=b.

    2个前提:

    1)只在非负整数范围内讨论两个数 m 和 n 的最大公约数,即 m, n ∈ N.

    2)0可以被任何数整除,但是0不能整除任何数,即 ∀x(x|0) and ∀x(0| x).


     

    1个引理:

    假设 k|a, k|b,则对任意的 x,y  ∈ Z, k|(xa+yb)均成立.

    证明:

      k|a => a=pk, k|b => b==qk (其中 p,q ∈ Z)

      于是有 xa+yb=xpk+yqk=(xp+yq)k

      因为 k|(xp+yq)k, 所以 k|(xa+yb)


     

    gcd的Euclid算法证明:

    命题:对任意 m, n ∈ N,证明gcd(m,n) = gcd(n, m mod n)

    证明:

      令 k=gcd(m,n),则 k|m 并且 k|n;

      令 j=gcd(n, m mod n), 则j|n 并且 j|(m mod n);

      对于m, 可以用n 表示为 m=pn+(m mod n);

      由引理可知 j|m(其中 x=p,y=1), 又 j|n,于是 j 是 m 和 n 的公约数(但不一定是最大的);

      因为 k 是 m 和 n 的最大公约数,所以必有 k≥j;

      通过另一种表示形式:(m mod n)=m-pn,同理可得:

      k|(m mod n),又k|n,于是 k 是 (m mod n) 和 n 的公约数(也不一定是最大的);

      同样由 j 是 n 和 (m mod n)最大公约数可以得到 j≥k;

      由常识,得出结论 k=j,

      即gcd(m,n) = gcd(n, m mod n) ,得证

    gcd算法的C语言实现:

    //递归版本
    int gcd(int m, int n)
    {
    	return n? gcd(n,m%n) : m;
    }
    
    //无递归版本
    int gcd(int m, int n)
    {
    	while (n)
    	{
    		n = m^n;
    		m = m^n;
    		n = (m^n)%m;
    	}
    	
    	return m;
    }
    
    //当m=0,n=0时, gcd(m,n)返回0, 我们可以视其为无穷大 (见评论)
    

    其他:

    gcd除了是“最大公约数(Greatest Common Divisor)”之外,还有什么意思?

    在上完这一章算法课的很长时间里,我看到gcd的第一反应永远都是“最大公约数”,但是如果把它当作是拼音的缩写的话,我们会看到它也就是我们敬爱的party。而且两者之间还有很大的联系:

    如果 k=gcd(m,n)并且 m>0,n>0,则可以知道 k≤m 并且k≤n,(当有一个数为0,比如n=0,那么 k=gcd(m,n)=m≥0=n)。即在m和n都是正整数的条件下,通过gcd运算(我们姑且把它看作是一个运算符吧)得到的结果一定不会大于原来的任意一个数字。

    而k=gcd(m,n)这一小段代码中,我可能犯了一个编程大计:代码没有可读性,变量名无法指示变量的含义和作用(但是本来就只是两个普通的数字,真想不出什么比较合适的变量名)。总之我们就给它们一个更好的名称吧:

    speech=gcd(myWords,policyOfParty);

    啥意思?就是说,我们最终讲出来的话(speech)的内容和含义,不会高于我们原本的意思(myWords),但也永远不能高于policyOfParty。

    当然这也是有例外的,比如当policyOfParty=0的时候,就有了speech=myWords。这大概就是言论自由的状态吧。

    不过活在世上,说话总是要有个度,因此也不是说什么好或什么不好,就比如,我再把代码改改也是一样的:

    action=gcd(behavior,moral);//行为不能超过道德

    joke=gcd(words,mentalCapacityOfOthers);//玩笑不能超过别人的心理承受能力

    ....


    哈哈,程序真有意思,总是能反应出现实的百态。

  • 相关阅读:
    学习笔记008之Task
    问题: ActivityManager: Warning: Activity not started, its current task has been brought to the front
    问题; No label views point to this text field with an android:labelFor="@+id/@+id/editTextNumber1" attribute
    学习笔记001之[Android开发视频教学].01_06_Android当中的常见控件
    学习笔记002之学习资料
    学习笔记001之环境配置
    设计模式之代理模式
    C# Enum转换
    Json数据序列化对象,及对象序列化为Json格式
    groupby以后取每组前n行
  • 原文地址:https://www.cnblogs.com/ider/p/gcd_euclid.html
Copyright © 2020-2023  润新知