题254.洛谷P1082 扩展欧几里得算法-[NOIP2012 提高组] 同余方程
一、关于线性同余方程的求解综述
1.对线性同余方程的解释
ax ≡ c (mod b)表示关于x的解集,其中x需满足(a*x)%b=c。求解该问题等价于求解方程ax+by=c(或ax%b==c%b),其中只要x的集合能够被求出,那么y的集合也可以通过x求出。
2.求解线性同余方程的方法:扩展欧几里得算法
(1)关于扩展欧几里得算法:
定义:正如其名,它是欧几里得算法的扩展,在得到整数a,b的最大公约数(我们通过欧几里得算法,即辗转相除法求解a,b最大公约数)后,还希望得到整数x,y,使得ax+by=gcd(a,b)
- 对于整数a>b,当b=0时,gcd(a,b)=a,此时x=1,y=0;(我们知道,欧几里得算法到最后是形参b=0,return a,即a就是等于gcd(a,b),此时又有ax+by=gcd(a,b),那么显然x得等与1,y=0)
- 假设ax+by=gcd(a,b),有x,y的解为x1,y1,则ax1+by1=gcd(a,b),此为①式;
- 由欧几里得算法可知,当b!=0时做的操作为gcd(b,a%b),即进入到的gcd的形参a=b,b=a%b,则可设bx2+(a%b)y2=gcd(b,a%b),此为②式;
- 又由欧几里得算法可知,当b!=0时做的操作为gcd(b,a%b),则gcd(a,b)=gcd(b,a%b),则结合①②式可知ax1+by1=bx2+(a%b)y2;
- 由于a%b=a-(a/b)*b(被除数a等于商a/b乘以除数b加上余数a%b,则余数a%b是等于被除数a减商a/b乘以除数b),则ax1+by1=bx2+(a-(a/b)*b)y2=ay2+bx2-(a/b)*by2,即ax1+by1==ay2+b(x2-(a/b)*y2),此为③式;
- 由恒等定理可知,③式成立则x1=y2,y1=x2-(a/b)*y2 ④;
- 这样我们就得到了x1,y1,即ax+by=c的一组解,x1,y1基于x2,y2;
- 以上操作同样以递归方式实现,是建立在欧几里得递归写法之上,当最后b=0时,递归结束,gcd(a,b)被求出,开始返回。设正处于扩欧函数调用过程中,则x2,y2在递归过程中处于更深的一层递归,x1,y1由那个更深的一层递归调用结束后的赋值语句得到,当最浅的一层递归调用结束时,形参a正等于原线性同余方程中的a,而b亦是如此,则对应的x1,y1即为一组解;
(2)求解线性同余方程
1)方程有整数解的充要条件为gcd(a,b)|c,即c为gcd(a,b)的倍数
2)上述扩欧过程求解了x,y,而对于线性同余方程,我们只需要那个x就好了
3)若x0,y0为方程ax+by=c的一组解,则方程任意解可表示为x=x0+b’t,y=y0-a’t,t∈任意整数,a’=a/gcd(a,b),b’=b/gcd(a,b)
4)特别的,若gcd(a,b)=1,且x0,y0为方程ax+by=c的一组解,则方程任意解可表示为x=x0+bt,y=y0-at,t∈任意整数
5)对于一般的求解线性同余方程问题,往往被要求求出一个最小整数解,该值为x=x0(x0 % t + t)%t,其中t=b/gcd(a,b),对于4)而言t=b
二、题目
三、题解
#include <bits/stdc++.h>
using namespace std;
int ex_gcd(int a,int b,int& x,int& y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int ans=ex_gcd(b,a%b,x,y);
int tmp=x;//x2
x=y;//对应之前解释中的④式,x1=y2
y=tmp-a/b*y;//y2=x2-(a/b)*y2,切记a,b不是一直等于原线性同余方程的a,b,只有最开始的扩欧函数的才是
return ans;
}
int main()
{
int a,b,x,y;
cin>>a>>b;
x=a,y=b;
ex_gcd(a,b,x,y);
x=(x%b+b)%b;//x=(x0%t+t)%t,t=b/gcd(a,b),由于题目说ax ≡ 1 (mod b),则方程有整数解需1是gcd(a,b)的倍数,那显然gcd(a,b)=1,t=b,由于b>0,此时x必为最小正整数解
cout<<x;
}
//二刷代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x=1,y=0;
return a;
}
ll d=ex_gcd(b,a%b,y,x);//这里直接交换x、y,方便计算
y-=a/b*x;
return d;
}
int main()
{
ll a,b;
cin>>a>>b;
ll x,y;
ex_gcd(a,b,x,y);
cout<<(x%b+b)%b;//求正余数操作:(x%b+b)%b
}