前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >欧几里得算法(辗转相除法),扩展欧几里得算法,乘法逆元,最小正整数解

欧几里得算法(辗转相除法),扩展欧几里得算法,乘法逆元,最小正整数解

作者头像
zy010101
发布2019-05-25 19:49:10
6.7K0
发布2019-05-25 19:49:10
举报
文章被收录于专栏:程序员程序员

欧几里得算法

欧几里得算法是用来求解两个不全为0的非负整数m和n的最大公约数的一个高效且简单的算法。该算法来自于欧几里得的《几何原本》。数学公式表达如下:

代码语言:javascript
复制
对两个不全为0的非负整数不断应用此式:gcd(m,n)=gcd(n,m mod n);直到m mod n为0时。m就是最大公约数

证明:我们假设有a,b两个不全为0的数,令 a % b = r; 那么有 a = kb + r. 假设a,b的公约数是d。记做 d|a,d|b,表示d整除a和b。r = a - kb; 给这个式子两边同除以 d,有 r/d = a /d - kb / d,由于d是a ,b的公约数,那么r/d 必将能整除,即:b 和 a%b的公约数也是d。故:gcd(a,b) = gcd(b, a % b)。到此为止,已经证明了a和b的公约数和 b 和 a % b的公约数相等。直到a mod b为0的时候(因为即使 b > a,经过 a % b后,就变成计算gcd(b,a)。因此,a mod b的值会一直变小,最终会变成0),此时gcd(m,0) = m。因为0除以任何数都是0,所以m是gcd(m,0)的最大公约数。根据上面已经证明的等式gcd(a,b) = gcd(b, a % b)。可得:m就是最大公约数。定理得证。

下面是C++代码实现欧几里得算法。

代码语言:javascript
复制
#include <iostream>
using namespace std;
int gcd(int m, int n);
int main()
{
	int m, n;
	cout << "请输入要计算的两个数,用空格隔开:\n";
	cin >> m >> n;
	cout << "最大公约数是:" << gcd(m, n);
	
	return 0;
}
int gcd(int m, int n)
{
	int r;
	while (0 != n)
	{
		r = m % n;
		m = n;
		n = r;
	}
	return m;
}

扩展欧几里得算法

欧几里得算法提供了一种快速计算最大公约数的方法。而扩展欧几里得算法不仅能够求出其最大公约数。而且能够求出m,n和其最大公约数构成的不定方程mx+ny=d的两个整数x,y(这里x和y不一定为正数)。

在欧几里得算法中,终止状态是n == 0时,这时候其实就是gcd(m,0);我们想从这个最终状态反推出刚开始的状态。由欧几里得算法可知。gcd(m,n) = gcd(n,m mod n);那么有如下表达式:

gcd(m,n) = m*x1+n*y1;

gcd(n,m mod n) = n*x2+(m - m/n*n)*y2;(此处的m/n表示整除,例如:6/4 = 1;所以:m mod n = m % n = m - m/n*n)

化简上式有:n*x2 + m * y2 - m/n*n*y2

= m*y2 + n*(x2 - m/n*y2)

与原式 gcd(m,n) = m*x1+n*y1;​​​​​​对比,容易得出:

x1 = y2;

y1 = x2 - m/n*y2;

根据上面的递归式和欧几里得算法的终止条件n == 0,我们可以很容易知道最终状态是m * x1 + 0 * y1 = m;故:x1 = 1;根据上述的递推公式和最终状态,可以写出代码如下:

代码语言:javascript
复制
#include<iostream>

using namespace std;
int exgcd(int m, int n, int &x, int &y);

int main()
{
	int x, y;
	int m, n;
	int gcd;
	cout << "请输入两个数字:\n";
	cin >> m >> n;
	cout << "满足贝祖等式" << m << "*x + " << n << "*y = " << (gcd = exgcd(m, n, x, y)) << endl;
	cout << "最大公约数是:" << gcd << endl;
	cout << "其中一组解是:x = " << x << "; y = " << y << endl;
	
	system("pause");
	return 0;
}
int exgcd(int m, int n, int &x, int &y)
{
	if (0 == n)		//递归终止条件
	{
		x = 1;
		y = 0;
		return m;
	}
	int gcd = exgcd(n, m%n, x, y);		//递归求解最大公约数。
	int temp = x;
	x = y;						//回溯表达式1:x1 = y2;
	y = temp - m / n * y;		//回溯表达式2:y1 = x1 - m/n * y2;
	return gcd;
}

上述的方程m*x+n*y=gcd(m,n);这个方程也被称之为“贝祖等式”。它说明了对a,b和它们的最大公约数d之间的线性丢番图方程。一定存在整数x,y使得m*x+n*y=gcd(m,n)成立。从这里也可以得出一个重要推论:

a,b互质的充要条件是方程ax+by = 1必有整数解。

现在来讨论一个更一般的方程:ax + by = c(a,b,c都是整数)。这个方程想要有整数解,那么根据扩展欧几里得算法我们知道,当且仅当m是d = gcd(a,b)的倍数时有解。同时有无穷多组整数解。

我们知道了线性丢番图方程ax + by = c有整数解的条件,并且根据上述算法,也能求出一组丢番图方程的解。但是这组解很可能包含负数。我们通常的需求是最小的特解。也就是这个不定方程的最小正整数解。

一般扩展欧几里得算法有如下应用:

求解乘法逆元

把a*x=1 ( mod p)称为a关于 1 mod p的乘法逆元。 它的解其实就相当于寻找方程 a*x+p*y=1 的解。根据乘法逆元的性质,只有当a与p互素,a关于模p的乘法逆元有解。如果时不互素,则无解。那么这个方程就是a,b互质的充要条件是方程ax+by = 1必有整数解。我们就可以使用扩展欧几里得算法求得其逆元。

代码语言:javascript
复制
int inv(int m, int n, int & x, int & y)
{
	int gcd = exgcd(m, n, x, y);
	if (1 != gcd)		//说明乘法逆元不存在
	{
		return -1;
	}
	else
	{
		return (x + n) % n;		//为了使余数一定为正数
        //模运算系统的特性
	}
}

这样就能求出两个互质的数的逆元了。

最小正整数解

设整数a,b,c;若方程ax+by = c的一组整数解为(x0,y0);那么它的任意组整数解都可以写成:(x0+kb',y0-ka').

其中a' = a / gcd(a,b);b' = b / gcd(a,b);k取整数即可。

代码语言:javascript
复制
int res(int a, int b, int c ,int &x, int &y)
{
	int gcd = exgcd(a, b, x, y);		//求出最大公约数以及满足等式mx+ny=gcd(m,n)的一组(x,y);
	if (0 != c % gcd)		//如果c不是gcd(m,n)的倍数,该丢番图方程无整数解。
	{
		return -1;			
	}
	x *= c / gcd;			//将x转化为方程ax+by=c的解。
	//先求出x的最小值。根据通解公式x = x0+kb'; b'= b / gcd(a,b);
	b /= gcd;				//求b'
	if (b < 0)
	{	
		b = -b;				//b为负数,则取绝对值
	}
	int r = x % b;			//求出最小整数解
	if (r <= 0)
	{
		r += b;				//求出最小正整数解
	}

	return r;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月10日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 欧几里得算法
  • 扩展欧几里得算法
    • 求解乘法逆元
    • 最小正整数解
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档