一、欧几里得算法(gcd)
欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。应用领域有数学和计算机两个方面。计算公式gcd(a,b) = gcd(b,a mod b)。——百度百科
欧几里得算法用于求两个数的最大公因数,在数论类题目中比较常见
他的核心公式为 gcd ( a , b ) = g c d ( b , a % b ) \operatorname{gcd}(a, b)=g c d(b, a \% b) gcd(a,b)=gcd(b,a%b)
即两个数的最大公因数和他们相所除取余数的最大公因数相等
通过这个公式的递归操作即可得到两数的最大公因数
代码:
long long gcd(long a, long b)//辗转相除法求最大公约数(a>b)
{
return (b == 0) ? a : gcd(b, a % b);//x/y=q...r
//(a,b)=(b,a%b) a和b的最大公约数等于b和余数的最大公约数
}
我们来证明一下
首先我们设 a = n 1 d b = n 2 d a=n_{1} d \quad b=n_{2} d a=n1db=n2d
即a,b都可以被d整除
接着设
a
=
n
b
+
c
(
a
÷
b
=
n
…
c
)
a=n b+c \quad(a \div b=n \ldots c)
a=nb+c(a÷b=n…c)
则有 c = a − n b = n 1 d − n ⋅ n 2 d = d ( n 1 − n n 2 ) c=a-n b=n_{1} d-n \cdot n_{2} d=d\left(n_{1}-n n_{2}\right) c=a−nb=n1d−n⋅n2d=d(n1−nn2)
由此我们可以看出c也可以被d整除,d是c=a%b的一个因数
我们接着证明d是b和c的最大公因数,我们知道
c
=
d
(
n
1
−
n
n
2
)
b
=
n
2
d
c=d\left(n_{1}-n n_{2}\right) \quad b=n_{2} d
c=d(n1−nn2)b=n2d
我们只需要证明 n 1 − n n 2 n_{1}-n n_{2} n1−nn2和 n 2 n_{2} n2互质就可以了
我们使用反证法法来证明,假设
n
1
−
n
n
2
=
q
⋅
t
n
2
=
p
⋅
t
(
t
>
1
)
n_{1}-n n_{2}=q \cdot t \quad n_{2}=p \cdot t \quad(t>1)
n1−nn2=q⋅tn2=p⋅t(t>1)
移项得到 n 1 = q t + n n 2 n_{1}=q t+n n_{2} n1=qt+nn2 ,将 a = n 1 d a=n_{1} d a=n1d 代入后得到
a = ( q t + n n 2 ) d a=\left(q t+n n_{2}\right) d a=(qt+nn2)d
去括号后将
b
=
n
2
d
b=n_{2} d
b=n2d 代入得到
a
=
q
t
d
+
n
1
b
a=q t d+n_{1} b
a=qtd+n1b
将我们开始假设的 n 2 = p ⋅ t n_{2}=p \cdot t n2=p⋅t 代入b中可得到 b = n 2 d = p ⋅ t ⋅ d b=n_{2} d=p \cdot t \cdot d b=n2d=p⋅t⋅d
经过代入和化简最终得到
a
=
t
⋅
d
(
q
+
n
1
p
)
b
=
t
⋅
d
⋅
p
\begin{array}{l} a=t \cdot d(q+n_{1} p) \\ b=t \cdot d \cdot p \end{array}
a=t⋅d(q+n1p)b=t⋅d⋅p
容易看出,此时a和b具有最大公因数td,和原来得到的d相悖,因此假设不成立, n 1 − n n 2 n_{1}-n n_{2} n1−nn2和 n 2 n_{2} n2互质成立,gcd(a,b)=gcd(b,a%b)成立
二、扩展欧几里得算法(exgcd)
扩展欧几里得算法主要用于在已知a,b的情况下求解一组x,y,使它们满足等式: a x + b y = g c d ( a , b ) = c ax+by=gcd(a,b)=c ax+by=gcd(a,b)=c
long long x, y; //目前方程真正的解
void exgcd(long long a, long long b)
{
//当前目的:求解 ax + by = gcd(a, b) 这么一个方程
if (b == 0) // a, b不断改变的过程中,b最终必然会成为0
{
//在 b = 0 时方程还要成立? 使 x = 1, y = 0 ,必然成立
x = 1;
y = 0;
return;
}
exgcd(b, a % b); //把下一层系数传进去(先求下一个方程的解 )
//现在我们已经拿到了下一个方程的解x, y
long long tx = x; //暂时存一下x,别丢了,tx即xn
x = y; //此时的x为xn-1 y为yn
y = tx - a / b * y;//等号左侧为yn-1 右侧为yn
}//xn-1=yn yn-1=xn-[a/b]*yn
证明如下,假设 a ⩾ b a \geqslant b a⩾b
显然当 b = 0 b=0 b=0时, gcd ( a , b ) = a \operatorname{gcd}(a, b)=a gcd(a,b)=a 此时x=1,y=0
当 b ≠ 0 b \neq 0 b=0时
根据欧几里得定理可得到
a
x
+
b
y
=
gcd
(
a
,
b
)
=
gcd
(
b
,
a
%
b
)
=
b
x
1
+
(
a
%
b
)
y
1
a x+b y=\operatorname{gcd}(a, b)=\operatorname{gcd}(b, a \% b)=b x_{1}+(a \% b) y_{1}
ax+by=gcd(a,b)=gcd(b,a%b)=bx1+(a%b)y1
也就是
a
x
+
b
y
=
b
x
1
+
(
a
%
b
)
y
1
=
b
x
1
+
(
a
−
b
⋅
⌊
a
/
b
⌋
)
y
1
a x+b y=b x_{1}+(a \% b) y_{1}=b x_{1}+(a-b \cdot⌊a / b⌋) y_{1}
ax+by=bx1+(a%b)y1=bx1+(a−b⋅⌊a/b⌋)y1
移项化简得到 a x + b y = a y 1 + b ( x 1 − ⌊ a / b ⌋ y 1 ) \left.a x+b y=a y_{1}+b\left(x_{1}-\lfloor a / b\right\rfloor y_{1}\right) ax+by=ay1+b(x1−⌊a/b⌋y1)
由等式恒等定理可以得到
{
x
=
y
1
y
=
x
1
−
⌊
a
/
b
⌋
y
1
\left\{\begin{array}{l} x=y_{1} \\ y=x_{1}-\lfloor a / b\rfloor y_{1} \end{array}\right.
{x=y1y=x1−⌊a/b⌋y1
x和y是本层 g c d ( a , b ) = a x + b y gcd(a,b)=ax+by gcd(a,b)=ax+by 的解,而 x 1 x_{1} x1和 y 1 y_{1} y1是在对gcd(a,b)按照欧几里得算法进行递归运算后下一层的解
此时求出来的
x
0
x_{0}
x0和
y
0
y_{0}
y0是一组特解,有时候满足ax+by=c的不止这一个特解,我们假设有x,y符合该式子,那么就有这样的公式
{
x
=
x
0
+
b
d
⋅
t
y
=
y
0
−
a
d
⋅
t
\left\{\begin{array}{l} x=x_{0}+\frac{b}{d} \cdot t \\ y=y_{0}-\frac{a}{d} \cdot t \end{array}\right.
{x=x0+db⋅ty=y0−da⋅t
符合该公式的所有x和y都是式子的解,
d
=
g
c
d
(
a
,
b
)
d=gcd(a,b)
d=gcd(a,b)
注意,欧几里得算法只对a,b大于0有效,当a<0时,我们需要将a,c同时反号,
例题一(k-rounding)
原题链接:https://vjudge.net/contest/479577#problem/A
1、题干
给定一个正整数n,要求求出一个最小的正整数,使得其末尾至少有k个连续的0,并且为n的倍数。
2、输入格式
只有一行,为n、k两个整数(1<=n<=1e9, 0<=k<=8)
3、输出格式
输出所求的最小的满足条件的正整数
注:(翻译来源洛谷)
4、样例
sample input1
375 4
sample output1
30000
sample input2
10000 1
sample output2
10000
sample input3
38101 0
sample output3
38101
sample input4
123456789 8
sample output4
12345678900000000
例题一题解
1、分析
本题实际上就是在求n 和 10^k的最小公倍数,而两数乘积等于二者的最大公因数和最小公倍数乘积
故我们只需将两数相乘除以其最大公因数即可
2、代码
#include <iostream>
using namespace std;
long long gcd(long x, long y)//辗转相除法求最大公约数(x>y)
{
return (y == 0) ? x : gcd(y, x % y);//x/y=q...r (x,y)=(y,r) x和y的最大公约数等于y和余数的最大公约数
}
int main()//两数乘积等于二者的最大公约数和最小公倍数乘积
{
long long n;
int k;
cin >> n >> k;
long long sum = 1;
for (int i = 1; i <= k; i++)
sum = sum * 10;
cout<<sum * n / gcd(sum, n);//相乘/最大公约数
return 0;
}
例题二(同余方程)
原题链接:https://vjudge.net/contest/479577#problem/D
1、题干
求关于x的同余方程 a x ≡ 1 ( m o d b ) a x \equiv 1 \pmod {b} ax≡1(modb)的最小正整数解。
2、输入格式
一行,包含两个正整数 a,b,用一个空格隔开。
3、输出格式
一个正整数 x 0 x_0 x0,即最小正整数解。输入数据保证一定有解。
4、样例
sample input 1
3 10
sample output 1
7
5、数据范围
对于 40%的数据, 2 ≤ b ≤ 1 , 000 2 ≤b≤ 1,000 2≤b≤1,000
对于 60%的数据, 2 ≤ b ≤ 50 , 000 , 000 2 ≤b≤ 50,000,000 2≤b≤50,000,000
对于 100%的数据, 2 ≤ a , b ≤ 2 , 000 , 000 , 000 2 ≤a, b≤ 2,000,000,000 2≤a,b≤2,000,000,000
例题二题解
1、分析
将题目给的式子改写成带余除法的形式并进行变形可知,本题实际上是在求 a x + b y = 1 ax+by=1 ax+by=1中满足等式的x的值,这里的y是引入的一个新数
根据扩展欧几里得原理,一定存在一组x,y的解使 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
根据裴蜀定理可知, a x + b y = m ax + by = m ax+by=m有解的必要条件是 m % gcd ( a , b ) = 0 m \% \operatorname{gcd}(a, b)=0 m%gcd(a,b)=0
由此可知 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1一定成立
由扩展欧几里得算法求出答案即可
求出x后有可能不是最小正整数解,需要进行答案处理
2、代码
#include <bits/stdc++.h>
using namespace std;
long long x, y; //目前方程真正的解
void exgcd(long long a, long long b)
{
//当前目的:求解 ax + by = gcd(a, b) 这么一个方程
if (b == 0) // a, b不断改变的过程中,b最终必然会成为0
{
//在 b = 0 时方程还要成立? 使 x = 1, y = 0 ,必然成立
x = 1;
y = 0;
return;
}
exgcd(b, a % b); //把下一层系数传进去(先求下一个方程的解 )
//现在我们已经拿到了下一个方程的解x, y
long long tx = x; //暂时存一下x,别丢了,tx即xn
x = y; //此时的x为xn-1 y为yn
y = tx - a / b * y;//等号左侧为yn-1 右侧为yn
}//xn-1=yn yn-1=xn-[a/b]*yn
int main()
{
long long a, b;
cin >> a >> b;
exgcd(a, b);
x = (x % b + b) % b; //我们求出来的x必然满足方程,但不一定是最小正整数解,所以要进行答案处理
//如果 x 太小就不断加 b 直到大于等于 0,太大则一直减 b
cout << x << endl;
return 0;
}