GDUT ACM2022寒假集训 专题四 A D(欧几里得/扩展欧几里得算法)(gcd/exgcd)

一、欧几里得算法(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=nc)

则有 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=anb=n1dnn2d=d(n1nn2)

由此我们可以看出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(n1nn2)b=n2d

我们只需要证明 n 1 − n n 2 n_{1}-n n_{2} n1nn2 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) n1nn2=qtn2=pt(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=pt 代入b中可得到 b = n 2 d = p ⋅ t ⋅ d b=n_{2} d=p \cdot t \cdot d b=n2d=ptd

经过代入和化简最终得到
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=td(q+n1p)b=tdp

容易看出,此时a和b具有最大公因数td,和原来得到的d相悖,因此假设不成立, n 1 − n n 2 n_{1}-n n_{2} n1nn2 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 ab

显然当 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+(aba/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(x1a/by1)

由等式恒等定理可以得到
{ 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=x1a/by1

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+dbty=y0dat
符合该公式的所有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} ax1(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 2b1,000

对于 60%的数据, 2 ≤ b ≤ 50 , 000 , 000 2 ≤b≤ 50,000,000 2b50,000,000

对于 100%的数据, 2 ≤ a , b ≤ 2 , 000 , 000 , 000 2 ≤a, b≤ 2,000,000,000 2a,b2,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值