这个应该是我在noip前就应该会的东西 ,但是当时也许只是记下了代码吧 ,现在有诸多的不理解。后来借着书和几篇博客弄懂了并小证了一下,鉴于网上有些博客关于这个的写的真的不好看,所以自己来总结一下,顺带以后也能看。
顺带一提,
gcd(a,b)
表示a,b的最大公约数。
欧几里德算法
辗转相除法求最大公约数问题,同可求最小公倍数。
既然是辗转相除法,自然就是%%%,%到互相整除为止。代码也很详尽简洁。
int regulargcd(int x,int y)
{
return y==0?x:(regulargcd(y,x%y));
}
最小公倍数为两数之积除以他们的最大公约数,相信大家都能明白。
扩展欧几里德算法
我之前一直困惑的是它为什么可以求不定方程的解,后来基本明白了:
给定一个形如方程
ax+by=d
,求它的整数解。
我们知道
a,b
为整数,
d
是整数,使得
所以说,假设我们有 d=gcd(a,b)∗t ,则方程 ax+by=d 的解为 agcd(a,b)x+bgcd(a.b)y=gcd(a,b) 的t倍,那么原问题转化为求方程 agcd(a,b)x+bgcd(a.b)y=gcd(a,b) 的解了。
再推到一般,若 a,b 互质,有 ax+by=1 有解。
然后接下来我们考虑特殊解的情况:
gcd(a,b) 递归的最终结果为 gcd(k,0) ,假定方程有解,那么最终有 a∗1+b∗0=gcd(a,b) ,即 a∗1+b∗0=1 ,即 x=1 , y=0 出现,这是在求 gcd 时递归形成的。若我们也这样地求解方程呢?
于是我们来找 ax+by=gcd(a,b) 与 bx′+(a%b)∗y′=gcd(b,a%b) 的关系,由于 a%b=a−(a/b)∗b (整数除法) 所以代入有
于是原方程解为
x=y′,y=x′−(a/b)∗y′
。
这样我们就能递归地计算方程的解了。
int extgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
else
{
int ans;
ans=extgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
}
不定方程不是有很多解吗?是的,这只是最小解。设最小解为 x0,y0 ,它的所有解为 x=x0+bgcd(a,b) 和 y=y0+agcd(a,b) 。(自己带到方程里展开就有恒等式,这里不证了)当 gcd(a,b)=1 时,有 x=x0+b , y=y0+a 。
while(x<=0)
{
x+=(b/res);
}
printf("%d\n",x);
例题:zoj3609求逆元
方法同上,AC代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
//zoj3609 AC
using namespace std;
int extgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
else
{
int ans;
ans=extgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-(a/b)*y;
return ans;
}
}
int regulargcd(int x,int y)
{
return y==0?x:(regulargcd(y,x%y));
}
int main()
{
int a,b;
int cas;
scanf("%d",&cas);
for(int z=0;z<cas;z++)
{
scanf("%d%d",&a,&b);
int x,y;
if(regulargcd(a,b)==1)
{
int res=extgcd(a,b,x,y);
if(x>0)
{
printf("%d\n",x);
}
else
{
while(x<=0)
{
x+=(b/res);
}
printf("%d\n",x);
}
}
else
{
printf("Not Exist\n");
}
}
return 0;
}
关于逆元,请移步逆元-wiki
谢谢看到这里