您的位置:首页 > 其它

拓展欧几里得算法——青蛙的约会

2018-02-03 11:04 232 查看
首先先来介绍一下什么是拓展欧几里得算法!
我们知道欧几里得算法利用gcd(a,b) = gcd(b , a%b)(非重点,可以百度证明过程)来求解a,b的最大公约数
int gcd( int a , int b)
{
if(b==0) returna;
else returngcd(b, a%b);
}
那什么是拓展欧几里得呢?和欧几里得算法有什么联系?实际上就是在欧几里得算法求公约数的过程中将x, y求出来!下面来说说x, y是什么鬼!

现在我们知道了 a 和 b 的最大公约数是 gcd ,那么,我们一定!!一定!!(数论知识)能够找到这样的 x 和 y 满足: a*x + b*y = gcd 这是一个不定方程,并且是有多解的。那我们能不能找出一条通式来表达所有的可能解呢?是可以的!只要我们找到一组特殊的解 x0 和 y0 那么,我们就可以用x0 和 y0 表示出整个不定方程的通解:

x = x0 + (b/gcd)*t (务必留意)

y = y0
– (a/gcd)*t

为什么不是:

x = x0 + b*t

y = y0
– a*t

b/gcd
是 b 的因子, a/gcd 是 a 的因子是吧?那么,由于 t的取值范围是整数,你说 (b/gcd)*t 取到的值多还是 b*t 取到的值多?同理,(a/gcd)*t 取到的值多还是 a*gcd 取到的值多?那肯定又要问了,那为什么不是更小的数,非得是 b/gcd 和a/gcd ?

注意到:我们令 B = b/gcd , A = a/gcd , 那么,A 和 B 一定是互素的吧?(仔细想想应该不难理解!)所以最小的系数就是 A 和 B 了!

现在,我们知道了一定存在 x 和 y 使得 : a*x +b*y = gcd , 那么,怎么求出这个特解 x 和 y 呢?只需要在欧几里德算法的基础上加点改动就行了。也就是开头说的求解x, y!

我们观察到:欧几里德算法停止的状态是: a= gcd , b = 0 ,那么,这是否能给我们求解 x y 提供一种思路呢?因为,这时候,只要 a = gcd 的系数是 1 ,那么只要 b 的系数是 0 或者其他值(无所谓是多少,反正任何数乘以 0 都等于 0 但是a 的系数一定要是 1),这时,我们就会有: a*1 + b*0 = gcd

这是最终状态,那我们是否可以从最终状态反推到最初的状态呢?上面不懂为什么没关系!往下看!

假设当前我们要处理的是求出 a 和 b的最大公约数,并求出 x 和 y 使得 a*x + b*y= gcd ,而我们已经求出了下一个状态:b 和 a%b 的最大公约数,并且求出了一组x1 和y1 使得: b*x1 +(a%b)*y1 = gcd , 那么这两个相邻的状态之间是否存在一种关系呢?

我们知道: a%b = a - (a/b)*b(这里的 “/” 指的是整除,例如 5/2=2 , 1/3=0),那么,我们可以进一步得到:

gcd = b*x1 + (a-(a/b)*b)*y1

= b*x1 + a*y1
– (a/b)*b*y1

= a*y1 + b*(x1
– a/b*y1)

对比之前我们的状态:求一组 x 和 y 使得:a*x + b*y= gcd ,是否发现了什么?

这里:

x = y1

y = x1
– a/b*y1
那我们怎么知道x1, y1呢?求出x2, y2!!!直到最终状态!
还是不懂具体看看代码,多想想,也许就能懂吧…….
在此之前先来几个定理,实际上就是把上面说的重新简洁地叙述了一遍
定理1 gcd(a,b)是ax+by的线性组合的最小正整数,x,y∈z;

定理2 如果ax+by=c,x,y∈z;则c%gcd==0;

定理3 如果a,b是互质的正整数,c是整数,且方程

ax+by=c(1)

有一组整数解x0,y0则此方程的一切整数解可以表示为

x=x0+bt;y=y0-at;t∈z;(2)


以上就是扩展欧几里德算法的全部过程,依然用递归写:



扩展欧几里德有什么用处呢?

求解形如 a*x +b*y = c 的通解,但是一般没有谁会无聊到让你写出一串通解出来,都是让你在通解中选出一些特殊的解,比如下面的青蛙约会!

根据题意,两个青蛙跳到同一个点上才算是遇到了,不是一个追上另一个啊!!!所以有 (x+m*t) - (y+n*t) = p * ll (p是周长,t是跳的次数,ll是a青蛙跳的圈数跟b青蛙的圈数之差。整个就是路程差等于纬度线周长的整数倍)
这里不懂就代个数看看!这也许就是传说中的抽象和建模吧!难点在这!

转化一下: (n-m)* t + p* ll= x
– y; 实际就是求解不定方程a*x+b*y=c!拓展欧几里得!

令 a = n-m, b = ll, c = gcd(a, b), d =x-y;

有 a * t + b * p= d; (1)

要求的是t的最小整数解。

用扩展的欧几里德求出其中一组解t0,p0,
并令c = gcd(a, b);

有 a * t0 + b * p0 = c; (2)

因为c =gcd(a, b),
所以 a * t / c是整数,b
* t / c 也是整数,所以 d / c
也需要是整数,否则无解。

(2)式两边都乘(d / c)
得 a * t0 *(d / c) + b* p0 * (d / c) = d;

所以t0 * (d / c)是最小的解,但有可能是负数。

因为a * ( t0 *(d / c) + b*n) + b * (p0 * (d / c)
– a*n) = d; (n是自然数)
用到了上面的通式!!!

所以解为 (t0 * (d / c) % b + b) % b;
下面是几个拓展题目:
ZOJ 3609 :http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4712
求最小逆元
ZOJ 3593
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3593
求最小的步数,处理特殊一点就过去了
HDU 1576
http://acm.hdu.edu.cn/showproblem.php?pid=1576
做点处理即可
HDU
2669 http://acm.hdu.edu.cn/showproblem.php?pid=2669
裸的扩展欧几里得
下面给出引用的博客链接:
拓展欧几里得详解:http://m.blog.csdn.net/article/details?id=7786595
大神题解:http://blog.chinaunix.net/uid-22263887-id-1778922.html

#include <iostream>
#define ll long long

using namespace std;

ll x, y;

ll ex_gcd(int a, int b)
{
if(b == 0){
x = 1;
y = 0; ///最终状态来临,我们给出x==1,y==0!回溯开始!
return a;
}
ll ans;
ans = ex_gcd(b, a%b);  ///一直递归,直到a==gcd,b==0的最终情况
ll temp = x;
x = y;
y = temp - a/b*y;
return ans;
}

int main( )
{
ll m, n, L;
while(cin>>x>>y>>m>>n>>L){
ll a = n-m;
ll b = L;
ll c = x-y;
ll gcd = ex_gcd(a, b);
if(c%gcd!=0) {cout<<"Impossible"<<endl;continue;}
else{
x*=c/gcd; ///注意了这里我们求出来的是 a*x + b*y = gcd的 x 的最小值,所以要两边乘c/gcd才是真的解
b/=gcd;   ///b也是一样的道理
if(b<0) b=-b;
ll result = x%b;   ///避免负数情况 (x0 * (c / gcd) % b + b) % b 这才是正解
if(result<=0) result+=b;
cout<<result<<endl;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: