算法学习笔记(9): 中国剩余定理(CRT)以及其扩展(EXCRT)
扩展中国剩余定理
讲解扩展之前,我们先叙述一下普通的中国剩余定理
中国剩余定理
中国剩余定理通过一种非常精巧的构造求出了一个可行解
但是毕竟是构造,所以相对较复杂
x \equiv a_1 \pmod{m_1} \\
x \equiv a_2 \pmod{m_2} \\
x \equiv a_3 \pmod{m_3} \\
\dots \\
x \equiv a_n \pmod{m_n}
\end{cases}
\]
对于上述同余方程组,首先需要满足 \(m_1, m_2, m_3, \dots, m_n\) 互质
令 \(m = \prod_{i=1}^{n} m_i, M_i = m / m_i,\ t_i\) 是线性同余方程 \(M_it_i \equiv 1 \pmod {m_i}\) 的一个解
那么上述方程组有整数解,为 \(x = \sum_{i=1}^n a_iM_it_i\)
证明:
由于所有 \(m_i\) 互质,所以 \(M_i = m/m_i\) 是除了 \(m_i\) 之外的所有模数的倍数,所以 \(\forall j \ne i, a_iM_it_i \equiv 0 \pmod {m_k}\)
所以代入 \(x = \sum_{i=1}^n a_iM_it_i\),原方程组成立
中国剩余定理给出了方程的一个特解。通解可以表示为 \(x + km (k \in \Z)\)。如果需要求最小的非负整数解,只需要让 \(x\) 对于 \(m\) 取模就是。
参考代码
#include <iostream>
typedef long long ll;
ll m[11], a[11], Mm[11], y[11];
// 扩展欧几里得算法,由于求出线性同余方程 Mi * ti = 1 (mod p) 的一个特解
// 上述同余式可以转化为 Mi * ti + k * p = 1
// 所以跑一个 extgcd(Mi, p, ti, k) 即可 (k没有用的)
void extgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1, y = 0;
return;
}
extgcd(b, a % b, y, x);
y -= (a / b) * x;
}
int main() {
int n; scanf("%d", &n);
ll M(1);
// 这里读入,并求出m
for (int i = 0; i < n; i++) {
scanf("%lld%lld", m + i, a + i);
M *= m[i];
}
// 求出Mi
for (int i = 0; i < n; i++) {
Mm[i] = M / m[i];
}
// 求出每一个不定方程的特解
for (int i = 0; i < n; i++) {
ll x, tmp, mi = m[i];
extgcd(Mm[i], mi, x, tmp);
y[i] = (x + mi) % mi;
}
// 求和获得最小正整数解
ll x(0);
for (int i = 0; i < n; i++) {
x = (x + a[i] * Mm[i] * y[i]) % M;
}
printf("%lld\n", x);
return 0;
}
扩展中国剩余定理
还是考虑上述式子
x \equiv a_1 \pmod{m_1} \\
x \equiv a_2 \pmod{m_2} \\
x \equiv a_3 \pmod{m_3} \\
\dots \\
x \equiv a_n \pmod{m_n}
\end{cases}
\]
此时,我们不保证 \(m_i\) 不一定两两互质,所以中国剩余定理不再适用
所以扩展中国剩余定理就开始发挥作用了
虽然说是扩展……但是两者思路很不一样
扩展中国剩余定理采用数学归纳法,或者说两两合并法
假如我们需要合并方程
\]
\]
我们将同余式改写
\]
\]
将两者相减,整理后可得
\]
由于我们已知 \(m_1, m_2, a_1, a_2\),也就是说我们只需要知道 \(k_1, k_2\) 其中任意一个,就可以求出这时的 \(x\) 是个什么东西了
所以,联想到了……扩展欧几里得算法
可以参考文章:算法学习笔记(1): 欧几里得算法及其扩展
我们令
d &= gcd(m_1, m_2) \\
c &= a_2 - a_1
\end{aligned}
\]
那么化简一下有:\(k_1 m_1 - k_2 m_2 = c\)
所以我们求出 \(k_1' \frac {m_1}d + k_2' \frac {m_2}d = 1\)
那么换回去之后
k_1 &= k_1' \frac cd \\
k_2 &= -k_2' \frac cd
\end{aligned}
\]
由于 exgcd(a, b, x, y) 求出的是 xa + yb = gcd(a, b) 的一组解……所以需要 \(\frac cd\)
令 \(l = lcm(m_1, m_2) = m_1 m_2 / gcd(m_1, m_2)\)
那么此时 \(x \equiv k_1' m_1\frac cd + a_1 \pmod l\)
也就是可以得到 \(x\) 的一个解集 \(x \in X = \{kl + x|k \in \R\}\)
也就是说,我们将上述两个式子合并成了
\]
那么考虑代码怎么写
typedef int data; // 方便换成long long
// 扩展欧几里得算法
inline data exgcd(data a, data b, data &x, data &y) {
if (b == 0) {
x = 1, y = 0;
return a;
}
data r = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return r;
}
// 最终的合并的结果会放在 a1, m1上
// 每一部分我会在后文详细的解释
inline bool merge(data& a1, data& m1, data a2, data m2) {
// section1: 为了防止溢出,需要适当的取模
data c = (a2 - a1) % m2;
if (c < 0) c += m2;
// section2: 通过拓展欧几里得算法求出上文中的 s = k1'
data d = gcd(m1, m2), s, t;
exgcd(m1 / d, m2 / d, s, t);
// +section2.5: 判断是否有解
if (c % d) return false;
// section3: 这里将s转化为上文中的 k
s = s / * c % m2;
if (s < 0) s += m2;
// section4: 正式合并
data lcm = m1 / d * m2;
a1 = (a1 + s * m1) % lcm;
if (a1 < 0) a1 += lcm;
m1 = lcm;
return true;
}
?:为什么在前三个部分放在了模 \(m2\) 的情况下计算
为了避免溢出,我们需要将 \(s\) 转化为最小的正整数解
那么需要有代码
(s % (m2 / d) + m2 / d) % (m2 / d)考虑到其实并不需要最小……所以为了方便,就直接是
(s % m2 + m2) % m2考虑优化第二个模运算(贼慢),所以换成了
if语句,就是上面代码的写法那么第一个部分为什么也可以取模呢?
考虑我们在第三部分放在了模 \(m_2\) 的意义下,所以我们可以把整个不定方程组放在模 \(m_2\) 的意义下,所以就可以把 \(c\) 对于 \(m_2\) 取模
?: 为什么在第二部分需要提前算出 \(gcd(m_1, m_2)\)
其实是不用的……只需要
data s, t; data d = exgcd(m1, m2, s, t);也可以算出正确答案……
?: 判断有解是如何判断的
有无解实际上就是拓展欧几里得算法有无解
而判断拓欧算法有无解,也就通过贝祖定理验证
也就是说,对于不定方程
ax + by = c如果
gcd(x, y) | c则有解,否则无解
NOTICE:在模板题上,由于可能会溢出……至于什么地方需要用龟速乘,请读者自行揣度
关于龟速乘:算法学习笔记(2): 逆元及其应用我提过一点
更详细的参考 快速乘总结 - 一只不咕鸟
算法学习笔记(9): 中国剩余定理(CRT)以及其扩展(EXCRT)的更多相关文章
- 学习笔记:中国剩余定理(CRT)
引入 常想起在空间里见过的一些智力题,这个题你见过吗: 一堆苹果,\(3\)个\(3\)个地取剩\(1\)个,\(5\)个\(5\)个地取剩\(1\)个,\(7\)个\(7\)个地取剩\(2\)个,苹 ...
- 数论算法 剩余系相关 学习笔记 (基础回顾,(ex)CRT,(ex)lucas,(ex)BSGS,原根与指标入门,高次剩余,Miller_Rabin+Pollard_Rho)
注:转载本文须标明出处. 原文链接https://www.cnblogs.com/zhouzhendong/p/Number-theory.html 数论算法 剩余系相关 学习笔记 (基础回顾,(ex ...
- 中国剩余定理(CRT)及其扩展(EXCRT)详解
问题背景 孙子定理是中国古代求解一次同余式方程组的方法.是数论中一个重要定理.又称中国余数定理.一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作<孙子算经>卷下第 ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- 中国剩余定理 CRT
中国剩余定理 CRT 正常版本CRT 要解的是一个很容易的东西 \[ \begin{aligned} x\equiv a_1(mod\ m_1)\\ x\equiv a_2(mod\ m_2)\\ . ...
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- Linux学习笔记(7)CRT实现windows与linux的文件上传下载
Linux学习笔记(7)CRT实现windows与linux的文件上传下载 按下Alt + p 进入SFTP模式,或者右击选项卡进入 命令介绍 help 显示该FTP提供所有的命令 lcd 改变本地上 ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- 中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结
中国剩余定理(CRT) & 扩展中国剩余定理(ExCRT)总结 标签:数学方法--数论 阅读体验:https://zybuluo.com/Junlier/note/1300035 前置浅讲 前 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
随机推荐
- 成功解决:Can‘t find Python executable “python“, you can set the PYTHON env variable.
今天跑公司新项目的时候.运行前端vue.报了一个关于python的错误.就离谱 1.问题报错全部代码 actual version of core-js. npm ERR! code 1 npm ER ...
- 如何在IDEA中创建Module、以及怎样在IDEA中删除Module?
文章目录 1.为何要使用Module? 2.Module的创建 3.如何从硬盘上删除module 1.为何要使用Module? 目前主流的大型项目都是分布式部署的,结构类型这种多Module结构.不同 ...
- go-zero docker-compose 搭建课件服务(一):编写服务api和proto
0.转载 go-zero docker-compose 搭建课件服务(一):编写服务api和proto 0.1源码地址 https://github.com/liuyuede123/go-zero-c ...
- LcdTools如何使用PX01进行EDP屏EDID比对及设置显示EDID比对结果
PX01点EDP屏在上电过程会自动读取屏EDID,那怎么进行EDID比对呢? LcdTools打开点屏工程,在上电时序函数中先用SetCmpEDID()指令设置EDID比对值,再调用CheckEDID ...
- Linux进程间通信(二)
信号 信号的概念 信号是Linux进程间通信的最古老的一种方式.信号是软件中断,是一种异步通信的方式.信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某个突发事件. 一旦产生信号 ...
- C#中下载项目中的文件
1.将需要下载的文档添加到项目的文件夹中 2.接口部分 public IActionResult DownLoad() { var filePath = Directory.GetCurrentDir ...
- vue中push()和splice()的使用方法
vue中push()和splice()的使用方法 push()使用 push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度.注意:1. 新元素将添加在数组的末尾. 2.此方法改变数组的长度 ...
- photoshop 2021 for mac安装教程,亲测可用!!!
小编分享下photoshop cc 2021 for mac 安装教程,适配M1芯片,让大家完美使用ps2021,畅享所有新功能Adobe Photoshop2021(简称PS) 新版本主要增加了Ne ...
- nrf9160 做modem—— 连接云(接入方式MQTT)
今天测试把nrf9160作为modem的例程Serial LTE Modem程序(后面简称slm),何为做modem,通俗来说就是将nrf9160作为无线模块,主控由其余MCU做,主控通过AT命令控制 ...
- 一个实用的 vite + vue3 组件库脚手架工具,提升开发效率
无论是 vue2 全家桶还是 vue3 + vite + TypeScript,组件库的使用几乎大家都会,但自己开发一个独立组件库就不是每个人都掌握的,因为搭建组件库的基础开发环境,就会让很多同学望而 ...