扩欧-扩展欧几里得 | 数论学习笔记

事故发生在上周的牛客比赛,我用某作业帮拍题AC了T1

于是我用同样方法做了今天的T2 推箱子, 取得4分的好成绩,还加了10多个特判

于是......我意识到不学数论是不行的, 那就先学一下扩展欧几里得吧

upd 2021.8.20 : 加了一些证明, 证明还是很重要的

裴蜀定理

  来看这么一个不定方程:ax + by = c , 对于该方程, 当且仅当c为gcd(a, b)的倍数时, 方程有解。

  证明方法,感兴趣读者可自行查阅更为详细的资料, 本文不再赘述。其实是我懒得证明

  upd:口糊一个证明, 因为ax, by肯定是gcd的倍数,所以c一定是gcd的倍数,至于充分性:c是gcd倍数时一定有解, 这个可以直接用exgcd的转移做数学归纳法

扩展欧几里得

  那么问题来了,如果我们不仅仅想要知道该方程是否有解,而且需要具体求出这些解该怎么办呢?

  这时候,就需要用到扩展欧几里得了,显然:该算法是欧几里得的扩展版本,可以在求gcd的时候顺便求出一组可行解。

  

  先来看看普通的gcd

int gcd(int a, int b) {
  if (b == 0) return a;
  return gcd(b, a % b);
}

  我们先假设gcd函数在求出a,b的gcd同时, 还能求出一组(x, y),使得ax+by=gcd(a, b)(注意这里不是=c)。

  那么我们在执行gcd(a, b)时,会先递归调用 gcd(b, a%b),并且求出一组x1, y1满足 b*x1 + (a%b)*y1 = gcd(b, a%b) .

  显然gcd(b, a%b)==gcd(a, b), 所以又可以写成 b*x1 + (a%b)*y1 = gcd(a, b)

  进一步,a%b = ,  带入得

  分别提出a, b:

    

     我们回到最开始,需要求出(x, y)使得 ax+by=gcd(a, b) , 根据上式

  x = y1 ,  y = x1-(a/b)*y1 

  这样就可以递归求解了,但还要考虑一下递归的边界, 由于递归的方式和gcd一样,所以先考虑一下gcd的边界。 gcd在b==0时返回, 此时 gcd(a, b) = a (因为b为0)。容易想到, a*1+b*0 = a = gcd(a, b), 所以这时我们直接返回即可,x=1, y=0。

  详见代码

  

int exgcd(int a, int b, int &x, int &y) {
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	int res = exgcd(b, a%b, x, y);//gcd的结果,同时得到(b, a%b)时的x,y 
	int l = x;
	x = y; // x = y1
	y = l - (a/b)*y; // y = x1 - (a/b)*y;
	return res;
}

  

  于是,我们就可以求出一组解 使得 ax+by=gcd(a, b)了。回到问题,我们要求的是 ax+by = c, 根据裴蜀定理,c为gcd(a, b)时才有解,否则直接特判无解。

  c为gcd(a, b)倍数时, 我们要将等式右边变成c,将式子两边同时乘上 c/gcd(a, b) 即可,得到原式的一组解 x*gcd, y*gcd 

  如果仍不理解, 读者可根据上文手推一遍式子

 求出所有整数解

  然而这里只有一组解,我们想要得到的是所有整数解,所以扩展欧几里得并不能解决这个问题 ,不做了(大雾

    我们可以将x加上一个b/gcd, 将y减去一个a/gcd,这样所得到的一组x,y仍是方程的解。

  为什么呢?

  a*x + b*y = c, 这是原式,分解开来

  a*x  ---> a*(x+b/gcd) ---> a*x + a*b/gcd  相当于增加了一个a*b/gcd;

  b*x 同理---> b*y - a*b/gcd ; 相当于减少了一个a*b/gcd;

  容易发现a*b/gcd 实际上就是a,b最小公倍数(lcm),也就是说,加上一个lcm,又减小一个lcm,式子值不变,等式当然还是成立, 所以仍是方程的解。反复进行该操作,即可得到所有的解

  upd: 这只是构造一组解, 没有证明为什么可以构造出所有解, 这里补上: 假设当前解为x, y, 那么如果要得到所有解, 我们可以尝试找到大于x的第一个解x1, 此时y为y1, 也就是说, 要找到最小的x1(x1 > x)使得等式成立 ax1+by1=ax+by, 我们设x2=x1-x, y2=y1-y, 也就是:a(x+x2)+b(y+y2)=ax+by,ax2=-by2,且x2最小,
也就是ax2最小,显然当ax2 = lcm(a, b) = by2时达到最小, 故可以得到所有解

  

  

  总结一下求解的过程: 先求出一组解,其他解都可以表示为 x+t(b/gcd) , y+t(a/gcd), 枚举所有t即可求出所有解(注意!!!t可以为负数,读者请仔细思考)

  这个方程肯定是有无限个解的, 所以题目只会让我们求出符合要求的解统计答案

 a, b...可以为负数吗?

  这个细节真的非常坑....

  前面所看到的代码, 也就是大多数博客所讲到的,并不能处理负数!!!

   可是,欧几里得并没有规定a,b,  c一定是是正整数,这时该怎么办呢?

  其实也不难想到,但一定要注意, 只要把a, b变成正的即可。

     如果a是负数,可以把问题转化成 (借一下百度百科的图)

  然后求出所有解, 把每个解的x取负,即可得到原方程的解。

 来看个题目 推箱子

  传送门

     

  

  

原文地址:https://www.cnblogs.com/ltdjcoder/p/13922694.html