編程需要相當紮實的數學基礎。
今天筆者要複習的是關於兩個數的最大公約數和最小公倍數的問題,同時會收錄一些習題進行練習。
最大公約數和最大公倍數的定義
最大公因數,也稱最大公約數、最大公因子,指兩個或多個整數共有約數中最大的一個。a,b的最大公約數記爲(a,b),同樣的,a,b,c的最大公約數記爲(a,b,c),多個整數的最大公約數也有同樣的記號。求最大公約數有多種方法,常見的有質因數分解法、短除法、輾轉相除法、更相減損法。與最大公約數相對應的概念是最小公倍數,a,b的最小公倍數記爲[a,b]。
常用結論
-
如果兩個自然數是互質數,那麼它們的最大公約數是1,最小公倍數是這兩個數的乘積。
例如8和9,它們是互質數,所以(8,9)=1,[8,9]=72。 -
如果兩個自然數中,較大數是較小數的倍數,那麼較小數就是這兩個數的最大公約數,較大數就是這兩個數的最小公倍數。
例如18與3,18÷3=6,所以(18,3)=3,[18,3]=18。 -
兩個自然數的最大公約數與它們的最小公倍數的乘積等於這兩個數的乘積。
例如12和16,(12,16)=4,[12,16]=48,有4×48=12×16,即(12,16)× [12,16]=12×16。
第三個結論在編碼中經常用到哦!
用輾轉相除法寫一個函數求兩個數num1,num2的最大公約數爲gcd(num1,num2),那麼這兩個數的最小公倍數爲num1*num2/gcd(num1,num2)
用輾轉相除法求最大公約數
兩個數的最大公約數是指能同時整除它們的最大正整數。
設兩數爲x、y,求x和y最大公約數 的步驟如下:
- 令p=max(x,y),q=min(x,y),則有p>=q;
- 用p除以q(a≥b),得 p%q=temp;
- 若 temp =0 ,則(x,y)=q;
- 若 temp !=0 ,則再令p=q,q=temp進行迭代,直到temp=0爲止
其最後一個餘數爲0的除數即爲(x,y)的最大公約數。
//輾轉相除法求最大公約數
int gcd(int x,int y)
{
int p = max(x,y);
int q = min(x,y);
int temp;
while(q!=0){
temp = p%q;
p = q;
q = temp;
}
return p;
}
根據gcd函數求最小公倍數lcm
根據結論,兩個自然數的最大公約數與它們的最小公倍數的乘積等於這兩個數的乘積。
即
lcm(x,y) * gcd(x,y) = x*y
所以lcm函數聲明及定義如下:
//最小公倍數=x*y/最大公約數
long long lcm(int x,int y)
{
long long ret;
ret = x;
ret = ret*y;
ret = ret/gcd(x,y);
return ret;
}
藍橋杯訓練題:5-1最小公倍數
題目來源
http://lx.lanqiao.cn/problem.page?gpid=T392
問題描述
編寫一函數lcm,求兩個正整數的最小公倍數。
樣例輸入
一個滿足題目要求的輸入範例。
3
5
樣例輸出
15
與上面的樣例輸入對應的輸出。
數據規模和約定
輸入數據中每一個數的範圍。
例:兩個數都小於65536。
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
//輾轉相除法求最大公約數
long long gcd(int x,int y)
{
int p = max(x,y);
int q = min(x,y);
int temp;
while(q!=0){
temp = p%q;
p = q;
q = temp;
}
return p;
}
//最小公倍數=x*y/最大公約數
long long lcm(int x,int y)
{
long long ret;
ret = x;
ret = ret*y;
ret = ret/gcd(x,y);
return ret;
}
int main()
{
int m,n;
cin>>m>>n;
cout<<lcm(m,n);
return 0;
}
2019藍橋杯選拔賽:最大公約數和最小公倍數問題
題目來源
http://acm.nefu.edu.cn/problemShow.php?problem_id=2067
Description
輸入2個正整數x和y (2<=x<=100000,2<=y<=1000000),求,滿足下列條件的p,q的個數?
條件:
p,q是正整數,且p和q的最大公約數是x,最小公倍數是y;
試求:滿足條件的所有可能的2個正整數的個數.
Input
輸入x和y
Output
輸出滿足條件的p,q的個數
Sample Input
3 60
Sample Output
4
Hint
對於樣例:有4種,
3,60
15,12
12,15
60,3
解題思路
顯然min(x,y)<=p,q<=max(x,y)
如果對p,q進行遍歷,也即是
int p,q;
int sum = 0;
for(p = min(x,y); p <= max(x,y); p++)
for(q = min(x,y); q <= max(x,y); q++){
if(gcd(p,q) == x && lcm(x,y) == y)
sum++;
}
這樣時間複雜度太高,很容易超時,其實筆者試過了,確實Time Limit Exceeded
不妨結合這一結論 p*q = gcd(p,q) * lcm(p,q),可以通過一次遍歷求出滿足條件p,q的個數。
此題中:
gcd(p,q) = x
lcm(p,q) = y
若已知p時,q要滿足條件,要符合pq = gcd(p,q) * lcm(p,q) = xy
所以有q = x*y/p
也即是:
int p,q;
int sum = 0;
for(p = min(x,y); p <= max(x,y); p++){
q = x * y / p;
if(gcd(p,q) == x && lcm(p,q) == y)
sum++;
}
AC代碼
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
//輾轉相除法求最大公約數
long long gcd(int x,int y)
{
int p = max(x,y);
int q = min(x,y);
int temp;
while(q!=0){
temp = p%q;
p = q;
q = temp;
}
return p;
}
//最小公倍數=x*y/最大公約數
long long lcm(int x,int y)
{
long long ret;
ret = x;
ret = ret*y;
ret = ret/gcd(x,y);
return ret;
}
int main()
{
long long x,y;
cin>>x>>y;
long long p,q;
int sum = 0;
for(p=min(x,y);p<=max(x,y);p++){
q = x*y/p;
if(gcd(p,q)==x && lcm(p,q)==y)
sum++;
}
cout<<sum;
return 0;
}