二元一次不定方程学习笔记

定义

含有两个未知数,且未知数项的次数都是 1 的不定方程就是二元一次不定方程,一般可以化成下面的形式:

ax+by=c

前置知识

裴蜀定理

定理:对于一个二元一次不定方程,当 gcd(a,b)|c 存在整数解。

证明:设 c=k×gcd(a,b) ,只需证明 ax+by=gcd(a,b) 有整数解,因为把等式两边同时乘 k 即可得到 a(x×k)+b(x×k)=c

a0=agcd(a,b) , b0=bgcd(a,b) ,则 a0b0, 则方程可以化为:

a0x+b0y=1

为了证明这个方程有整数解,我们可以构造一组解。设 a01a0 在模 b0 意义下的逆元,我们可以得到:

a0a01b0×a0a01b0=1

因为 a0a011(modb0) ,所以设 a0a01=kb0+1a0a01b0=k ,代入原式得:

(kb0+1)b0=1

1=1

therefore 对于方程 a0x+b0y=1 一定有整数解 x=a01,y=a0a01b0 ,故方程 ax+by=c 一定有整数解,证毕。

辗转相除法

辗转相除法又称欧几里得(Euclid)算法,可以在 O(logn) 的时间复杂度内求出 gcd(a,b) ,设 a>b ,过程如下:

gcd(a,b)={ba=0gcd(b,amodb)otherwise

这就是大多数时候求 gcd 的方法。

求解二元一次不定方程

扩展欧几里得算法 (exEuclid)

对于一个二元一次不定方程 ax+by=gcd(a,b) 来说,我们可以按照辗转相除法的方法来顺便求出 x,y

举个例子,当 a=39,b=15 时,执行辗转相除法的过程如下:

39,1515,99,66,33,0

我们先考虑最后一个方程,设 a0,b0 为当前的 a,bx0,y0 为当前的 x,y ,比如上面 a0 就等于 3 ,得到方程:

a0x0+b0×y0=gcd(a,b)

b0=0 方程变为

a0x0=gcd(a,b)

所以现在 x0=gcd(a,b)÷a0 ,而 a0 就是 gcd(a,b) ,所以x0=1

现在我们往上推,设 a1,b1 为上一次的 ab , 所以根据辗转相除法, a0=b1,b0=a1modb=a1a1b1×b1 ,所以方程变为:

b1×x0+(a1a1b1×b1)×y0=gcd(a,b)

a1b1 提出来得:

a1×y0+b1×(x0a1b1×y0)=gcd(a,b)

这样新的解就是 x1=y0,y1=x0a1b1×y0
,然后就可以再往上推了。这就是扩展欧几里得算法,时间复杂度和辗转相除法一样,都是 O(logn)

code

//a>=b,返回gcd(a,b),并修改x,y,使得ax+by=gcd(a,b) 
int exEuc(int a, int b, int &x, int &y) {
	if (b == 0) {//结束条件 
		x = 1, y = 0;
		return a;	
	}
	int t = exEuc(b, a % b, x, y);
	int x0 = x, y0 = y;//暂记录上次的结果 
	x = y0, y = x0 - (a / b) * y0;
	return t; 
}

方程的多个解

对于一个二元一次方程 ax+by=c ,要么没有整数解,要么有无穷多个整数解和有限个正整数解,对于任意一组整数解 x0.y0 和整数 ka0=agcd(a,b),b0=bgcd(a,b),c0=cgcd(a,b) ,满足于下面的算式的都是解:

{xk=x0+k×b0yk=y0k×a0

所有整数解都可以这样算出来,这也很好理解,下面证一下:

a0x0+b0y0=c0

a0x0+b0y0+a0×k×b0a0×k×b0=c0

a0(x0+k×b0)+b0(y0k×a0)=c0

a0xk+b0yk=c0

得证。

一些题目

P5656 【模板】二元一次不定方程 (exgcd)

题意:有点多,自己看吧。

思路

对于 c|gcd(a,b),说明无解。

对于有解,先求出 x,y 的最小正整数值,而 x 的最小正整数值与 y 的最大正整数值是一组解,反之亦然,所以判断这样算出的 x,y 的最大正整数值是否为正整数,不是直接输出最小值,是的话正整数解的个数就是 x 的最大值与最小值的差除以 b0 加一。

code

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
ll exEuc(ll a, ll b, ll &x, ll &y) {
	if (b == 0) {
		x = 1, y = 0;
		return a; 
	}
	ll t = exEuc(b, a % b, x, y);
	ll x0 = x, y0 = y;
	x = y0, y = x0 - (a / b) * y0;
	return t;
}
ll a, b, c;
void solve() {
	scanf("%lld%lld%lld", &a, &b, &c);
	ll x, y;
	ll g = exEuc(a, b, x, y);
	if (c % g != 0)
		printf("-1\n");
	else {
		x = x * (c / g), y = y * (c / g);
		ll a0 = a / g, b0 = b / g;
		ll x0 = (x > 0 ? -1ll * floor(1.0 * x / b0) : 1ll * ceil(1.0 * (0 - x) / b0));
		ll y0 = (y > 0 ? -1ll * floor(1.0 * y / a0) : 1ll * ceil(1.0 * (0 - y) / a0));
		x0 += (x + x0 * b0 == 0), y0 += (y + y0 * a0 == 0);
		if (y - a0 * x0 <= 0 && x - b0 * y0 <= 0)
			printf("%lld %lld\n", x + x0 * b0, y + y0 * a0);
		else {
			ll x1 = x - b0 * y0, y1 = y - a0 * x0;
			x0 = x + x0 * b0, y0 = y + y0 * a0;
			printf("%lld %lld %lld %lld %lld\n", (x1 - x0) / b0 + 1, x0, y0, x1, y1);
		}
	}
}
int main() {
	int T;
	scanf("%d", &T);
	while (T--)
		solve();	
	return 0;
} 
posted @   rlc202204  阅读(249)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示