第2章 数字之魅——数字中的技巧

2.7最大公约数问题

问题:求两个数的最大公约数。

对于该问题:首先映入眼帘的就是两个数n m中寻找一个最小的值。然后从该值遍历到1.一旦 n%i==0&&m%i==0 那么i就是这个最大公约数啦。原理不言而喻。代码就不附上了。(不纳入方法好了)

 

方法1:就是比较经典的欧几里德算法。其中本质上的原理是这样的。gcd(n,m)表示n和m的最大公约数。

1:gcd(n,m) = gcd(n%m,m)  (n>m)

2:gcd(0,a) = a 这是合法的。因为0可以当做被除数(废话,但是为了严谨一点再次说明一下)。

式1是递推关系。式2是判断终点。 完全符合递推关系的定义啊。(不知道Kunth能不能求出这个的闭合形式呢。)

 

先解释一下原理:

对于1:令r=n%m 即证明 gcd(n,m) = gcd(r,m). 其中n = km+r (k属于正整数,因为n>m所以k!=0).那么r = n-km.

也就是说要证明:gcd(n,m) = gcd(n-km,m)。也就是证明n-km和m的最大公因子是n和m的最大公因子

根据素数表达式。n-km有且只有 n与m的公因子。那么简单就能知道n-km和m的最大公因子就是n和m的最大公因子。得证。

代码实现如下:

int GCD(int n,int m)
{
    if(m==0){return n;}
    if(n>m){return GCD(m,n%m);}
    else{return GCD(n,m%n);}
}
编程之美---欧几里得求最大公约数

 

方法2:对于gcd(n,m) = gcd(n-km,m) 再次思考这个等式。会发现。只要n-km>0即可。那么n-m完全是可以的。(即令k=1)而且可以避开求余这个比较耗费时间的运算。

具体实现。只要将上述方法1的代码中的%更改为-号即可。其实在这里思考会觉得n-km是在极大地缩小数值。但是会经过求余。而且此方法2对于类似于10000000 1这样的数据也是同样不适宜的。

 

方法3:对于最大公因子是如何产生的进行分析(从本质上的一个角度去考虑问题)。一个数由素数的乘积构成。那么由此可知。

一个最大公因子是由2个数之间公共的素数构成的。也就是说有

性质1:gcd(kn,km) = k*gcd(n,m);

反过来说非公共部分的素数因子是没有用的。也就是说有

性质2:gcd(n,p*m) = gcd(n,m) 其中p和n互素。

综合以上性质1 和性质2 以及方法2构成第3种方法。

如果n,m都是偶数。那么2就是他们的一个公共素数因子。可以利用性质1.如果n,m都是奇数那么我们可以做差gcd(n,m) = gcd(n-m,m) (n>m) 也就变成一个是奇数一个是偶数

对于一个是奇数一个是偶数的情况 利用性质2。令偶数除以2.

 结尾还是gcd(0,a) = a

代码实现如下:

int GCD(int n,int m)
{
    if(n<m)
    {
        int t = n;
        n = m;
        m = t;
    }

    if(m==0){return n;}
    if(n&1==1) //n为奇数
    {
        if(m&1==1)
        {
            return GCD(n-m,m);
        }
        else
        {
            return GCD(n,m>>1);
        }
    }
    else
    {
        if(m&1==1)
        {
            return GCD(n>>1,m);
        }
        else
        {
            return 2*GCD(n>>1,m>>1);
        }
    }
}
编程之美---利用公因子本质去求最大公因子

 

个人思想收获:

对于方法2,是方法1的变式,但是万变不离其宗。均是在公因子本质上的思考。也许我们在考虑问题的时候,应该更多地去敲击本质上的东西。而非只是针对形式上的特点。

posted @ 2015-03-31 00:08  Milkor  阅读(314)  评论(0编辑  收藏  举报