同余方程——扩展欧几里得の板子

人类为什么需要扩欧?

扩展欧几里得这玩意儿吧,原本上来说应该是求方程 ax+by=gcd(a,b) 的解的。但是考虑到计算机中常用的逆元,它可以表示为 ax1(mod b) ,那么这个式子可以变形为 axby=1

然后呢,在自然数中,只有互质的两个数才会有逆元。这个问题也比较容易证明,设存在两个不互质的正整数 a,b 满足 ax1(mod b) ,那么设这两个数的最大公约数为 g(g1) ,那么 a=pg b=qg 。于是原式可以表示为 pgxqgy=1 ,提一个公因式 g 就变成了g(pxqy)=1。因为 g1 ,所以我们可以惊讶地发现 (pxqy) 变成了一个分数!那然后这就不大好了对吧。。。不是在自然数范围内了对吧。。。。= =

好吧回到原来的话题。 axby=1 (其实写成 ax+by=1 也无所谓对吧反正这个y在这里没啥用好像)可以再进一步写成 ax+by=gcd(a,b) 。然后这就到了扩欧能解决的范围内了!
求逆元基本上是扩欧最常见的用途啦。

扩欧为什么能求 ax+by=gcd(a,b) 的解?

扩展欧几里得之所以得名,就是因为它跟平常求gcd的欧几里得算法有密不可分的关系。在欧几里得算法中我们通过把 gcd(a,b) 转化成 gcd(b,a%b) ,实现一步步递归求解;在扩展欧几里得中,我们同样可以做这样的变形。

既然有式子 ax+by=gcd(a,b) ,那么同样就有式子 bx+(a%b)y=gcd(b,a%b) 。因为 gcd(a,b)=gcd(b,a%b) ,那么如果我们知道了第二个式子的解,就可以通过变形得到第一个式子的解。

首先考察递归终止情况: b=0 时, a 就是原来调用的a b 的最大公因数,这个和普通的欧几里得算法的原理是一样的。那么我们要求的式子ax+by=gcd(a,b)就变成了 ax=a ,显然 x=1

那么当我们知道 bx+(a%b)y=gcd(b,a%b) 的解的时候,我们可以对式子作出如下变形:( 为了方便,这里把 gcda,b 直接用 g 代替)。

  1. a%b变成 aa/bb (这里的除法是整除),然后把式子展开变成 bx+ay(a/bb)y=g

    • 进一步整理变成 ayb(xa/by)=g 。这样我们可以看出来这个式子和前面要求的式子 ax+by=g 的形式是一样的,那么我们就求出来了原来的式子的一组解: x=y,y=xa/by 。注意是一组解。这种不定方程的解有很多,我们只是求出来了一组。
    • 这样每次把 gcd(a,b) 换成 gcd(b,a%b) 递归下去就可以出解了。这里贴一个板子题:CodeVS1200 同余方程。注意这个题要求最小正整数解,而求出来的x可能为负数,所以要把它模成正的。

      这就是扩展欧几里得求逆元的一个标准模式啦!

      #include<cstdio>
      #include<cstdio>
      #include<cstring>
      #include<algorithm>
      using namespace std;
      int a,b,x,y;
      int exEuclid(int a,int b)
      {
          if (b==0)
          {
              x=1;y=0;return a;
          }
          int t,ans=exEuclid(b,a%b);
          t=x;
          x=y;
          y=t-(a/b)*y;
          return ans;
      }
      int cal(int a,int b)
      {
          int ans,g=exEuclid(a,b);
          b=abs(b);
          ans=x%b;
          if (ans<=0) ans+=b;
          return ans;
      }
      int main()
      {
          scanf("%d%d",&a,&b);
          printf("%d",cal(a,b));
          return 0;
      }
      那我还想要更多的解怎么办?

      对于方程 ax+by=gcd(a,b) 来说,用扩展欧几里得求出的一组解是满足 |x|+|y| 最小的(这个东西愚蠢的ATP也不会证明啦。。是看了TA学长的博客才知道的。。学长有一大——坨证明,ATP看着就头大。。贴下学长的blog在这里啦)。
      然后如果一直把 x 加上b/g y 减去a/g,那么得到的仍然是一组满足条件的解。正确性把 x=x+b/g,y=ya/g 代进去就会发现方程仍然是成立的啦。

      扩欧会出现无解情况吗?

      如果你是要求 ax+by=gcd(a,b) 的话那肯定有解的啦。。好像有个什么贝祖定理啥的是讲这个事的啦。。
      但是如果你要求 ax+by=c ,随便扔一个c过去的话肯定没那么简单啦。。首先扩欧必须要求的是 ax+by=gcd(a,b) ,那也就是说当 gcd(a,b) 不是1的时候这个式子两边肯定可以约掉一个 gcd(a,b) 。那如果发现 c 不能整除gcd(a,b)就无解了对吧。。
      但是就算约掉了一个 gcd(a,b) ,它也不一定能化成标准的 ax+by=1 的形式,可能是 ax+by=c 。这也没什么关系,求出 ax+by=1 的解以后乘上c’就好了!

      仍然是贴两道题啦
      • BZOJ1477 青蛙的约会
        照着题意稍微推一下式子就有了扩欧的方程啦。注意判断无解。
      • BZOJ1407 Savage
        也是裸的扩欧题目,不过各种对于正负数啊啥的判断好像比上一个题麻烦一点。

      (还真是只有两道题= =)

      最后再提一句

      扩展欧几里得还有一个非常简短的形式啦!是出自fye学姐的板子!
      这里就贴一下zyf2000的博客里的一个链接啦。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值