zoukankan      html  css  js  c++  java
  • 『笔记』数学数论(三)

    \[\Huge{(三)线性同余方程、逆元} \]

    线性同余方程

    定义

    形如 \(ax \equiv c \pmod b\) 的方程被称为 线性同余方程(Congruence Equation)

    求解方式

    定理 1

    方程 \(ax+by=c\) 与方程 \(ax \equiv c \pmod b\) 是等价的,有整数解的充要条件为 \(\gcd(a,b) \mid c\)

    根据定理 \(1\) ,方程 \(ax+by=c\),我们可以先用扩展欧几里得算法求出一组 \(x_0,y_0\),也就是

    \[ax_0+by_0=\gcd(a,b) \]

    两边同时除以 \(\gcd(a,b)\)

    \[\dfrac{ax_0}{\gcd(a,b)} + \dfrac{by_0}{\gcd(a,b)}=1 \]

    乘以 \(c\) ,得到

    \[\dfrac{acx_0}{\gcd(a,b)} + \dfrac{bcy_0}{\gcd(a,b)} = c \]

    然后就找到了方程的一个解。

    定理 2

    \(\gcd(a,b)=1\),且 \(x_0\)\(y_0\) 为方程 \(ax+by=c\) 的一组解,则该方程的任意解可表示为:

    \[x=x_0+bt\\y=y_0-at \]

    且对任意整数 \(t\) 都成立。由此即可以求出方程的所有解。

    但在实际问题中,我们往往被要求求出一个最小整数解,也就是一个特殊解

    \[x=(x \bmod t+t) \bmod t \]

    其中 \(t=\dfrac{b}{\gcd(a,b)}\)

    代码:

    int ex_gcd(int a, int b, int &x, int &y)
    {
    	if (!b)
    	{
    		x = 1;
    		y = 0;
    		return a;
    	}
    	int t = ex_gcd(b, a % b, x, y);
    	int tmp = x;
    	x = y;
    	y = tmp - a / b * y;
    	return t;
    }
    bool liEu(int a, int b, int c, int &x, int &y)
    {
    	int t = ex_gcd(a, b, x, y);
    	int k = c / t;
    
    	if (c % t != 0)
    		return false;
    	x *= k;
    	y *= k;
    	return true;
    }
    

    逆元

    定义

    逆元(素),是指一个可与取消另一个元素运算的元素。数学中逆元有加法逆元和乘法逆元(乘法中的倒数)

    本篇文章只介绍乘法逆元。/dk

    乘法逆元

    • \(\pmod P\) 意义下,一个数 \(a\) 如果有逆元 \(x\) ,那么除以 \(a\) 相当于乘以 \(x\) (进化版 \(\cfrac{1}{a}\) ?)

    • \(\pmod P\) 意义下, \(a\) 存在逆元的充要条件事 \(p \ne 1\) ,且 \(a\)\(p\) 互质

    计算逆元

    拓展欧几里得

    不难得出,\(ax \equiv c \pmod b\) 等价于 \(a \times x + b \times y = c\) ,那么显然当 \(\gcd(a,b) \ne 1\) 时,上述方程无解。

    也就是说,使得 \(a \times x + b \times y = c\) 有解的充要条件是 \(c \% \gcd(a, b) =0\)

    一般情况下,上式能够计算出无数组合法解,但是题目往往要求计算出最小的那组解。我们可以首先求出一个特殊解 \(x_0\) ,那么最小的解便是 \(x_0 \% b\)

    正确性?

    首先,\(x\) 的通解是 \(x_0 + b \times t\) 吗?

    那么,也就是说, \(a\) 关于 \(b\) 的逆元是一个关于 \(m\) 同余的,那么根据最小整数原理,一定存在一个最小的正整数,它是 \(a\) 关于 \(b\) 的逆元,而最小的肯定是在\((0 , b)\) 之间的,而且只有一个,这就好解释了。

    \(b\) 是负数的时候,我们取 \(b\) 的绝对值就行了,当 \(x_0\) 是负数的时候,他模上 \(b\) 的结果仍然是负数(在计算机计算的结果上是这样的,虽然定义的时候不是这样的),这时候,我们仍然让 \(x_0\)abs(b) 取模,然后结果再加上 abs(b) 就行了,于是,我们不难写出下面的代码求解一个数 \(a\) 对于另一个数 \(b\) 的乘法逆元:

    int cl(int a, int b)
    {
    	int x, y;
    	int t = ex_gcd(a, b, x, y);
    	if (1 % t != 0)
    		return -1;
    	x *= 1 / t;
    	int ans = x % abs(b);
    	if (ans <= 0)
    		ans += abs(b);
    	return ans;
    }
    

    快速幂法

    因为 \(ax \equiv 1 \pmod b\)

    又由费马小定理可得 \(ax \equiv a^{b-1} \pmod b\)

    所以 \(x \equiv a^{b-2} \pmod b\)

    代码:

    int Qpow(int a, int b)
    {
    	int ans = 1;
    	a = (a % p + p) % p;
    	while (b)
    	{
    		if (b & 1)
    			ans = (a * ans) % p;
    		a = (a * a) % p;
    		b >>= 1;
    	}
    	return ans;
    }
    

    线性求逆元

    求出 \(1,2,...,n\) 中每个数关于 \(p\) 的逆元。

    显然有 \(1^{-1} \equiv 1 \pmod p\)

    证明:

    对于 \(\forall p \in \mathbf{Z}\),有 \(1 \times 1 \equiv 1 \pmod p\) 恒成立,故在 \(p\)\(1\) 的逆元是 \(1\)

    其次对于递归情况 \(i^{-1}\),令 \(k = \lfloor \frac{p}{i} \rfloor\)\(j = p \bmod i\),有 \(p = ki + j\)

    则有

    \[ki+j \equiv 0 \pmod p \]

    两边同时乘 \(i^{-1} \times j^{-1}\)

    \[\begin{aligned} kj^{-1}+i^{-1} &\equiv 0 &\pmod p\\ i^{-1} &\equiv -kj^{-1} &\pmod p \end{aligned} \]

    代入 \(j = p \bmod i\)

    \[p = ki + j \]

    又有

    \[i^{-1} \equiv -\lfloor\cfrac{p}{i}\rfloor (p \bmod i)^{-1} \pmod p \]

    由于 \(p \bmod i < i\),则在迭代中我们完全可以假设我们已经知道了所有的模 \(p\) 下的逆元 \(j^{-1}, j < i\)

    故我们就可以推出逆元,利用递归的形式,而使用迭代实现:

    \[i^{-1} \equiv \begin{cases} 1 , &i = 1, \\ -\lfloor\cfrac{p}{i}\rfloor (p \bmod i)^{-1}, &\text{otherwises}. \end{cases} \pmod p \]

    代码:

    inv[1] = 1;
    for (int i = 2; i <= n; ++i)
    {
    	inv[i] = (p - p / i) * inv[p % i] % p;
    }
    

    通过 \(p-\lfloor \cfrac{p}{i} \rfloor\) 来防止出现负数。

    另外,根据线性求逆元方法的式子

    \[i^{-1} \equiv -kj^{-1} \pmod p \]

    递归求解 \(j^{-1}\), 直到 \(j=1\) 返回 \(1\)

    其实还可以利用记忆化来避免多次递归导致的重复,但是这样求 \(1,2,...,n\) 中所有数的逆元的时间复杂度仍是 \(O(n)\)

    线性同余方程、逆元 完结!!

  • 相关阅读:
    编写 grunt 插件经验
    Sencha Touch 手机移动开发框架 HTML5 项目压缩方案;
    随笔 编辑推荐 上头条了, 贴出来做个记念!
    Javascript 俄罗斯方块 游戏代码解释!
    30天自制操作系统(NASM+GCC版)
    Logisim 打不开的解决方案(Windows10)
    Kali Linux 2020通过UEFI硬盘安装(免u盘)
    开源一个自制的ORM框架,基于Java原生JDBC(应该是全网首个吧)
    书单
    前端技术文章收集
  • 原文地址:https://www.cnblogs.com/Frather/p/14658515.html
Copyright © 2011-2022 走看看