扩展欧几里得算法模板(希望永远不要搞懂了)

来源:互联网 发布:手机淘宝查看历史价格 编辑:程序博客网 时间:2024/06/06 18:03

扩展欧几里得

上述谈到的最大公约数算法是数学家欧几里德提出的,同时,他也提出了扩展欧几里德算法来解决整数二元一次不定方程问题。

整数二元一次不定方程

形如a*x+b*y=c(a,b均不为0)的方程,a,b,c都是整数,求(x,y)的整数解。

1 判断是否有解

整数二元一次不定方程有解的充分必要是gcd(a,b)|c。如果不能整除则无解。

2 扩展欧几里德求特解

欧几里德给出了计算a*x+b*y=gcd(a,b)的解法

  1. long long exgcd(long long a,long long b,long long &x,long long &y)
  2. {
  3. if (b==0) { x=1,y=0; return a; }
  4. long long d=exgcd(b,a%b,x,y);
  5. long long tmp=x;
  6. x=y;
  7. y=tmp-a/b*y;
  8. return d;
  9. }

3 通解

a*x+b*y=gcd(a,b)在有解并求出特解(x,y)的情况下,通解可以表示为 


对于一般式a*x+b*y=c,如果有解,只需把a*x+b*y=c的特解乘上c/gcd(a,b)即可得到其特解,通解还是一样的公式。

4 例子

求出特解和一个通解

  1. int main(int argc, char* argv[])
  2. {
  3. long long a,b,c;
  4. scanf("%lld%lld%lld",&a,&b,&c);
  5. long long x,y,d;
  6. d=exgcd(a,b,x,y);
  7. if (c%d!=0) printf("No solution!\n");
  8. else
  9. {
  10. a/=d,b/=d,c/=d;
  11. x*=c,y*=c;
  12. printf("特解: %lld %lld\n",x,y);
  13. printf("一个通解: %lld %lld\n",x+b,y-a);
  14. }
  15. return 0;
  16. }

请思考一下,如何求出不定方程的正整数解个数。

 依然很简短,相比欧几里德算法,只是多加了几个语句而已。

    这就是理论部分,欧几里德算法部分我们好像只能用来求解最大公约数,但是扩展欧几里德算法就不同了,我们既可以求出最大公约数,还可以顺带求解出使得: a*x + b*y = gcd 的通解 x 和 y

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

    求解形如 a*x +b*y = c 的通解,但是一般没有谁会无聊到让你写出一串通解出来,都是让你在通解中选出一些特殊的解,比如一个数对于另一个数的乘法逆元

    什么叫乘法逆元?

    

    这里,我们称 x 是 a 关于 m 的乘法逆元

    这怎么求?可以等价于这样的表达式: a*x + m*y = 1

    看出什么来了吗?没错,当gcd(a , m) != 1 的时候是没有解的这也是 a*x + b*y = c 有解的充要条件: c % gcd(a , b) == 0

    接着乘法逆元讲,一般,我们能够找到无数组解满足条件,但是一般是让你求解出最小的那组解,怎么做?我们求解出来了一个特殊的解 x0 那么,我们用 x0 % m其实就得到了最小的解了。为什么?

可以这样思考:

    x 的通解不是 x0 + m*t 吗?

    那么,也就是说, a 关于 m 的逆元是一个关于 m 同余的,那么根据最小整数原理,一定存在一个最小的正整数,它是 a 关于m 的逆元,而最小的肯定是在(0 , m)之间的,而且只有一个,这就好解释了。

    可能有人注意到了,这里,我写通解的时候并不是 x0 + (m/gcd)*t ,但是想想一下就明白了,gcd = 1,所以写了跟没写是一样的,但是,由于问题的特殊性,有时候我们得到的特解 x0 是一个负数,还有的时候我们的 m 也是一个负数这怎么办?

    当 m 是负数的时候,我们取 m 的绝对值就行了,当 x0 是负数的时候,他模上 m 的结果仍然是负数(在计算机计算的结果上是这样的,虽然定义的时候不是这样的),这时候,我们仍然让 x0 对abs(m) 取模,然后结果再加上abs(m) 就行了,于是,我们不难写出下面的代码求解一个数 a 对于另一个数 m 的乘法逆元:

    




0 0