数论学习笔记:同余基础

目前我学习了一些很基础的数论算法和思想,其中主要关于同余。

一:不定方程

先来看这样一个方程:
$$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\),则有:

\[bx_1\ +\ ry_1\ =\ d \]

\(r\ =\ a\ -\ bq\) 代入:

\[bx_1\ +\ (a\ -\ bq)\ y_1\ =\ d \]

展开,按 \(a,\ b\) 整理:

\[ay_1\ +\ b(x_1\ -\ qy_1)\ =\ d \]

定睛一看,这不正是我们要的 \(ax + by = d\) 的形式吗,于是解的转移就很显而易见了:

\[now_x = lst_y\ ,\ now_y = (lst_x - (a\ /\ b)\times lst_y) \]

这里要注意,最后一层的 \(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\) 为此方程的另一组解,有:

\[ax_0+by_0=ax_1+by_1=d \]

移项:

\[a(x_0-x_1)=-b(y_0-y_1) \]

\(x_0-x_1=k\)\(y_0-y_1=t\),代入:

\[ak=-bt \]

又设 \(a'=\frac{a}{d}\)\(b'=\frac{b}{d}\),显然此时 \(a',\ b'\) 互质。

将等式两边同除 \(d\) 得:

\[a'k=-b't \]

所以 \(b'\mid a'k\)。因为 \(a',\ b'\) 互质,所以得 \(b'\mid k\)
\(k=b'm=\frac{b}{d}m\)
所以:

\[a'\frac{b}{d}m+\frac{b}{d}t=0 \]

两边同时除以 \(\frac{b}{d}\)

\[a'm+t=0 \]

所以

\[t=-a'm=-\frac{a}{d}m \]

因为 \(m\) 最小便是 \(1\)
步长此时便显然了:
\(x\) 的步长是 \(\frac{b}{d}\)\(y\) 的步长则是 \(-\frac{a}{d}\),当然符号可以反过来。
我们就可以通过枚举步长来求出所有解了。
要求出最小的正 \(x\),可以用 \((x_0\ mod\ b'\ +\ b')\ mod\ b'\),若为 \(0\) 则答案就是 \(b'\)
\(y\) 同理。

同余方程

什么是同余方程呢,就是形如这样的方程:

\[ax \equiv b \pmod m \]

它的意思是 \(a\ mod\ m\ =\ b\ mod\ m\)
看到它不要懵,想想我们之前的不定方程,我们可以把它转化成下面的样子:

\[ax+km=b \]

因为 \(km\) 一定是 \(m\) 的倍数,不影响余数,所以一定存在 \(k\) 满足。
瞪大眼睛一看,这不正是不定方程吗?故我们可以轻松解出它。

posted @ 2026-08-18 19:45  Vicco_Li  阅读(2)  评论(0)    收藏  举报