浅谈拓展欧几里得算法、最小正整数解与乘法逆元

欧几里得:

首先要明确欧几里得算法,就是我们所说的辗转相除法。
就是求两个数a,b的最大公因数gcd(a,b)

ll gcd(ll a,ll b)    //欧几里得算法 
{
    
                        //辗转相除法 
	if(a%b==0) return b;
	return gcd(b,a%b);
}

拓展欧几里得算法

通俗来说就是求二元一次方程 Ax+By=gcd(A,B) 的(x,y)的所有的解。(假设A,B已知)
我们的方法是通过这组方程的一个特解,得出他的通解。
那么就有两个问题
1.如何得到他的特解?
2.如何得由特解得到通解?****

1.找特解

由欧几里得算法可以将gcd(A,B)写成gcd(B,A%B)。
得到一个新的方程

Bx2+(A%B) * y2=gcd(B,A%B); -------------1

A%B=A-(A/B)*B;
再次改写方程

Bx2+[A-(A/B) * B ] y2=gcd(B,A%B);*------------2

当gcd(B,A%B)一直向下写,直到A%B=0 时;
由1号方程得
Bx2=B;
这时候我们得出方程的一组特解为x2=1,y2=0;
此时我们已经解决了第一个问题找到方程组的特解。

2.如何由特解得出通解
继续观察式子 2 。我们既然已经得出特解x2与y2。那么我们要是知道x2与x的关系,y2与y的关系,我们就能得出通解(x,y)。

现在对 2 式进行整理得到:

Ay2+B * [x2-(A/B)*y2]=gcd

原式为 Ax+By=gcd
此时我们可以得到通解与特解的关系

x=y2;
y=x2-(A/B)*y2

因为我们求特解的过程是一步一步递推下去的,所以我们求通解的过程就是一个回溯的过程。
我们拿一个3x+5y=1的例子来说


这里(2,-1)就是方程3x+5y=1的一组解
**总结:求通解的过程中主要还是运用我们上面得出的通解与特解的关系,因为每一步A,B系数是不同的所以要一步一步的由特解回溯到通解

拓展欧几里得
#include
#include
#include
#include
using namespace std;
const int N=1e6+10;
typedef long long ll;
typedef unsigned long long ull;
ll exgcd(ll a,ll b,ll &x,ll &y) {
if(b==0) {
x=1;
y=0;
return a;
}
ll res= exgcd(b,a%b,x,y);
ll t;
t=x;
x=y;
y=t-a/b*y;
return res;
}
int main() {
ll a,b,x,y;
cin>>a>>b;
ll anss = exgcd(a,b,x,y);
cout<<anss<<" “<<x<<” "<<y;
return 0;
}

我们再考虑一个问题:
怎么由一组通解得到多个解?

那肯定是x,y的值一增一减才能保证等式
ax+by=gcd的成立
我们知道a,b最小公倍数的等于(a*b)/gcd(a,b)
那么当x=x+b/gcd(a,b),y=y-a/gcd(a,b)的时候代入原式
得到 a *(x+b/gcd(a,b)) + b *( y-a/gcd(a,b))
不难看出每次让x或者y的值增减一个a,b的最小公倍数后,原式的值是保持不变的。
那么我们可以得到x或者y的变化最小周期就为b/gcd(a,b)或者a/gcd(a,b)
另外,也不难得出如果Ax+By=C,如果C%gcd(A,B)!=0,那么方程组一定无解

由此我们引出最小正整数解
**

最小正整数解

**
拓展欧几里得主要应用于求解方程的解
ax≡b(mod)l
ax-ly=b;
由上面拓展欧几里得算法我们可以求出x,但是x可能为负数,或者x并不是一个最小的正整数解
我们得到了x的周期,在该式子中周期T=l/gcd(a,l);
我们可以选择这样一种处理方式
x=((x%T)+T)%T;
保证x为最小正整数解

乘法逆元

在一个模系p中只有包含[1,p-1]的所有整数,所以在模系p中计算除法的时候由于无法整除出现精度问题,所以我们引入乘法逆元
ax≡1(mod)p
这时x为在模系p中a的逆元
学习完拓展欧几里得后
转化一下
ax-pk=1
a的逆元x的存在条件为gcd(a,p)=1
同样用拓展欧几里得的代码可以求出a的逆元x;

**拓展欧几里得,乘法逆元,最小整数解**
#include <iostream>
#include <cstring>
#include <algorithm>
#include<map>
using namespace std;
const int N=1e6+10;
typedef long long ll;
typedef unsigned long long ull;
l 
ll exgcd(ll a,ll b,ll &x,ll &y) {
    
    
	if(b==0) {
    
    
		x=1;
		y=0;
		return a;
	}
	ll res=	exgcd(b,a%b,x,y);
	ll t;
	t=x;
	x=y;
	y=t-a/b*y;
	return res;
}
int main() {
    
    
	// ax-pk=1
	//p =1000000007
	ll a,b,x,y;
	cin>>a>>b;
	exgcd(a,b,x,y);
	ll ans=exgcd(a,b,x,y);
// ax+by=c
x=x*c/gcd(a,b);
	ll t=b/ans;
	x=(x%t+t)%t;
	cout<<x;
	return 0;
}

欢迎指正

猜你喜欢

转载自blog.csdn.net/wmy0536/article/details/103984653