数论学习笔记:同余基础
目前我学习了一些很基础的数论算法和思想,其中主要关于同余。
一:不定方程
先来看这样一个方程:
$$ax + by = c$$
(其中 \(a,b,c\) 是已知常数)
也就是说,方程的未知数的数量大于方程数,这就叫不定方程。
上面这个就是典型的二元一次不定方程。
我们可以设 \(d = gcd(a, b)\),则 \(d\mid a,\ d\mid b\),则 \(d\mid ax + by\),所以 \(d\mid c\)。
然后可得,当且仅当 \(d\mid c\) 时,\(ax + by = c\) 有解。换言之,要使此方程有解,则 \(gcd(a, b, c) = gcd(a, b)\)。 这就是裴蜀定理。
然后怎么做呢?
我们可以使用一个叫 \(exgcd\) 的东东(此东东非彼东东)。
exgcd 求特解
所谓 \(exgcd\),无非就是在 \(gcd\) 的框架上进行扩展。
它是干嘛的呢? 它是用来求 \(ax + by = d\) 的一组特解 \(x_0,\ y_0\) 的。
我们设 \(q = \lfloor a\ /\ b \rfloor\),\(r = a\ mod\ b\ =\ a\ -\ bq\),\(d = gcd(a,\ b)\)。
按照普通辗转相除法的递归流程,下一层应该是 \((b,\ r)\)。
那么假设我们已经递归求出了下一层的特解 \(x_1,\ y_1\),则有:
将 \(r\ =\ a\ -\ bq\) 代入:
展开,按 \(a,\ b\) 整理:
定睛一看,这不正是我们要的 \(ax + by = d\) 的形式吗,于是解的转移就很显而易见了:
这里要注意,最后一层的 \(x = 1\), \(y = 0\), 因为此时的 \(a\) 就是 \(d\) 所以当 \(x = 1,\ y = 0\) 时自然是特解。
当递归结束后,此时的 \(now_x,\ now_y\) 便是特解。
初版代码
int a, b;
int lst_x, lst_y, now_x, now_y;
int exgcd(int a, int b) {
if (b == 0) { // 此时是最后一层
lst_x = now_x = 1, lst_y = now_y = 0; // 设置边界
return a; // 此时 a 即是 d
}
int d = exgcd(b, a % b);
now_x = lst_y, now_y = lst_x - (a / b) * lst_y; // 设置当前解
lst_x = now_x, lst_y = now_y; // 注意顺序
return d; // 返回 d
}
但是我们想,每次 \(exgcd\) 要用四个变量,太麻烦了,怎么优化呢?
因为 C++ 中有个叫地址传递的方式,可以直接修改值,递归返回后就能拿到 \(lst_x, lst_y\),此时再做更新就可以了,注意因为 \(now_x = lst_y\) 我们实际上可以把这一层的 \(y\) 直接传进去就好了。
int a, b;
int x_0, y_0;
int exgcd(int a, int b, int &x, int &y) { // 传递地址
if (b == 0) { // 此时是最后一层
x = 1, y = 0 // 设置边界
return a; // 此时 a 即是 d
}
int d = exgcd(b, a % b, y, x); // 此时 y = lst_x, x = lst_y
y -= (a / b) * x; // 设置当前解
// 因为是传递地址,实际上 now_x 已经 是 lst_y 了
return d; // 返回 d
}
int d = exgcd(a, b, x_0, y_0);
然后我们就得出了 \(ax + by = d\) 的一组解了。
利用特解求出所有解
根据裴蜀定理,\(d\mid c\),故我们在等式两边同时乘上 \(c\ /\ d\) 便能得到 \(ax + by = c\) 的一组特解了。
注意,因为若 \(a,\ b\) 中有一个是 \(0\) 则方程化为一元一次方程,若都为 \(0\) 且 \(c\) 不为 \(0\) 则无解,若 \(c = 0\) 无数解。所以接下来的讨论皆建立在 \(a \neq 0 ,\ b \neq 0\) 的基础上。
那有人说,不定方程的解肯定不止一组啊,怎么推出其他组呢?
我们找了几组规律会发现,这个相邻的解两两之间的步长似乎是一样的,怎么证明呢?
我们设 \(x_1,\ y_1\) 为此方程的另一组解,有:
移项:
设 \(x_0-x_1=k\),\(y_0-y_1=t\),代入:
又设 \(a'=\frac{a}{d}\),\(b'=\frac{b}{d}\),显然此时 \(a',\ b'\) 互质。
将等式两边同除 \(d\) 得:
所以 \(b'\mid a'k\)。因为 \(a',\ b'\) 互质,所以得 \(b'\mid k\)。
则 \(k=b'm=\frac{b}{d}m\)。
所以:
两边同时除以 \(\frac{b}{d}\) 得
所以
因为 \(m\) 最小便是 \(1\)
步长此时便显然了:
\(x\) 的步长是 \(\frac{b}{d}\),\(y\) 的步长则是 \(-\frac{a}{d}\),当然符号可以反过来。
我们就可以通过枚举步长来求出所有解了。
要求出最小的正 \(x\),可以用 \((x_0\ mod\ b'\ +\ b')\ mod\ b'\),若为 \(0\) 则答案就是 \(b'\)。
\(y\) 同理。
同余方程
什么是同余方程呢,就是形如这样的方程:
它的意思是 \(a\ mod\ m\ =\ b\ mod\ m\)
看到它不要懵,想想我们之前的不定方程,我们可以把它转化成下面的样子:
因为 \(km\) 一定是 \(m\) 的倍数,不影响余数,所以一定存在 \(k\) 满足。
瞪大眼睛一看,这不正是不定方程吗?故我们可以轻松解出它。

浙公网安备 33010602011771号