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的变式,但是万变不离其宗。均是在公因子本质上的思考。也许我们在考虑问题的时候,应该更多地去敲击本质上的东西。而非只是针对形式上的特点。