最大公约数——辗转相除法
辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的差的最大公约数,这是一个递归过程。
辗转相除法更像辗转相减法,因为辗转一次可能要减好几次,就变成了除法(取余操作)。
执行过程
假设要求自然数a、b的最大公约数,商依次为q0, q1, ..., qn, 余数依次为r1, r2, ..., rn,则执行过程如下:
a = q0 * b + r0;
b = q1 * r0 + r1;
r0 = q2 * r1 + r2;
r1 = q3 * r2 + r3;
...
rn-3 = qn-1 * rn-2 + rn-1;
rn-2 = qn * rn-1 + rn;
如果rn = 0; 则rn-1即为a、b的最大公约数。
证明
辗转相除法的正确性可以通过两步来证明。首先,算法的最终结果rn−1同时整除a和b。因为它是一个公约数,所以必然小于或者等于最大公约数g。然后,任何a和b的公约数都能整除rn−1。所以g一定小于或等于rn−1。两个不等式只在rn−1 = g是同时成立。具体证明如下:
1、证明rn-1是a和b的一个公约数,即rn-1能够a和b。
因为rn-2 = qn * rn-1 + rn、rn = 0,所以
rn-2 = qn * rn-1;
因为rn−1能够整除rn−2,所以也能够整除rn-3:
rn-3 = qn-1 * rn-2 + rn-1 = qn-1 * (qn * rn-1) + rn-1 = (qn-1 * qn + 1) * rn-1;
同理可证rn−1可以整除所有之前步骤的余数,包括a和b,即rn−1是a和b的一个公约数,rn−1 ≤ g。
2、证明任何整除a和b的自然数g(也就是a和b的公约数)都能整除rn-1:
根据定义,a和b可以写成g的倍数:a = mg、b = ng,其中m和n是自然数。
因为r0 = a − q0 * b = mg − q0 * ng = (m − q0 * n)g,所以g整除r0。
同理可证g整除每个余数r1, r2, …, rn-1。
综上所述,g = GCD(a, b) = GCD(b, r0) = GCD(r0, r1) = … = GCD(rn−2, rn−1) = rn−1。
C语言实现
int function(int a, int b) { if(a == 0) return b; if(b == 0) return a; return function(b, a % b); }
实际上,最大公约数对浮点数也是适用的:
#define feq(a, b) (fabs(a - b) < 1E-4) double fgcd(double a, double b){ if (feq(a, 0)) return b; if (feq(b, 0)) return a; return fgcd(b, fmod(a, b)); }