本篇博客主要介绍最大公约数及其求解方法。
最大公约数
首先,最大公约数是什么?
按照定义,最大公约数是两个数的公共因子中最大的一个。所以两个数最大公因数一定是确定的,同时最大公因数只能对于两个以上的数存在。
求解
1.枚举因数
按照定义,我们可以知道,两个数的最大公因数一定是这两个数的公因数,所以很容易想到,当我们将每一个公因子求出来时,其中的最大因子一定是最大公因数。
由此可以得到一个朴素算法:对于
n
n
n,
m
m
m两个数,枚举小于等于
n
\sqrt{n}
n
的每一个数,当这个数是
n
n
n的因子时,判断这个数是不是
m
m
m的因子,如果是,那么记录该数,并判断它是否是已经找到的数中的最大值。
当然,可以选择性地只枚举小于等于
min
(
n
,
m
)
\sqrt{\min(n , m)}
min(n,m)
的数,但是实际优化程度不大,仅在
n
,
m
n , m
n,m相差较大时才有明显效果。
int gcd(int n, int m)
{
int maxn = 1;
for(int i=2; i<sqrt(n)/*sqrt(min(n , m))*/; i++)
{
if(n%i==0)
{
if(m%i==0)
{
if(maxn < i)
maxn = i;
}
}
}
return maxn;
}
2.构造
上述方法明显多扫描了许多数字,所以我们可以考虑构造最大公因数。
既然最大公因数是两数的最大的公因数,那么最大公因数一定包含两数所有的公共质因子,所以我们可以先将这两个数分解质因子再用质因子构造最大公因数。
int f[Size], q[Size] , qn[Size][Size][2], tot[Size];
int top = 0;
void make_q(int n)
{
for(int i = 2 ; i <= n ; i++)
{
if(!f[i])
{
f[i] = i;
q[top++] = i;
}
for(int j = 0 ; q[j] <= f[i] && j < top ; j++)
{
f[i * q[j]] = q[j];
if(q[j] > n / i)
break;
}
}
}
void devide(int n , int num)
{
int temp = sqrt(n);
for(int i = 0; i < top ; i++)
{
if(n == 1)
break;
if(n % q[i] == 0)
qn[tot[num]][num][0] = q[i];
while(n % q[i] == 0)
{
n /= q[i];
qn[tot[num]][num][1]++;
}
tot[num]++;
}
if(n > 1)
{
qn[tot[num]][num][0] = n;
qn[tot[num]][num][1] = 1;
tot[num]++;
}
}
int gcd(int n , int m)
{
make_q(10000);
devide(n , 0);
devide(m , 1);
int ans = 1;
for(int i = 0 , j = 0 ; i < tot[0] || j < tot[1]; )
{
if(qn[i][0][0] == qn[j][1][0])
{
ans *= min(qn[i][0][1] , qn[j][1][1]) * qn[i][0][0];
i++;
j++;
}
else
{
if(qn[i][0][0] > qn[j][1][0])
j ++;
else
i ++;
}
}
return ans;
}
3.欧几里得算法
这可以说是最常用也最容易敲的算法了(至少我不知道更好的)。
首先,对于两个数
n
,
m
n, m
n,m,设他们的公因子为
a
a
a,那么
n
n
n和
m
m
m可以表示为
n
=
k
1
×
a
n = k_1 \times a
n=k1×a和
m
=
k
2
×
a
m = k_2 \times a
m=k2×a,那么
n
−
m
n - m
n−m 就可以表示为
(
k
1
−
k
2
)
×
a
(k_1 - k_2)\times a
(k1−k2)×a,也就是说该数也是
n
,
m
,
n
−
m
n , m , n - m
n,m,n−m的最大公因子。所以
a
a
a很明显也是
n
,
m
,
m
n,m,m
n,m,m mod
n
n
n的最大公因子,由此我们就可以想到一种算法:
对于任意一组
n
,
m
n , m
n,m而言,我们可以将其转换为求
n
=
m
,
m
=
n
n = m, m = n
n=m,m=n mod
m
m
m的解,以此类推,直到
m
=
0
m = 0
m=0为止,而这种情况明显是在上一级状况下
n
n
n是
m
m
m的倍数的情况下才能成立,所以此时
n
n
n即为最大公约数。
代码如下
int gcd(int n , int m)
{
if(n < m)
swap(n , m);
return m ? gcd(m , n % m) : n;
}