当前位置: 首页 > article >正文

数据结构与算法学习笔记----中国剩余定理

数据结构与算法学习笔记----中国剩余定理

@@ author: 明月清了个风
@@ first publish time: 2025.1.17

ps⭐️一道中国剩余定理的扩展题,在做这道题之前了解之前的逆元和扩展欧几里得算法能够更好的帮助理解(因为中间涉及求逆的运算)

Acwing 204. 表达整数的奇怪方式

[原题链接](204. 表达整数的奇怪方式 - AcWing题库)

给定 2 n 2n 2n个整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an m 1 , m 2 , ⋯   , m n m_1,m_2,\cdots,m_n m1,m2,,mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ [ 1 , n ] , x ≡ m i ( m o d a i ) \forall i \in[1,n], x \equiv m_i \pmod{a_i} i[1,n],xmi(modai)

输入格式

第一行包含整数 n n n

2 , ⋯   , n + 1 2,\cdots,n+1 2,n+1行:每 i + 1 i+1 i+1行包含两个整数 a i a_i ai m i m_i mi,数之间用空格隔开。

输出格式

输出最小非负整数 x x x,如果 x x x不存在,则输出 − 1 -1 1

数据范围

1 ≤ a i ≤ 2 3 1 − 1 1 \le a_i \le 2^31 - 1 1ai2311,

0 ≤ m i ≤ a i 0 \le m_i \le a_i 0miai,

1 ≤ n ≤ 25 1 \le n \le 25 1n25

所有 m i m_i mi的最小公倍数在 64 64 64位有符号整数范围内。

思路

中国剩余定理其实是一个公式,我们先给出这个定理:

中国剩余定理,又称孙子定理,是中国古代求解一次同余式组的方法,也是数论中的一个重要定理。它最早记载于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,题为“物不知数”问题,原文如下:

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

用现代数学语言描述,即求解满足以下同余方程组的整数 x x x

{ x ≡ 2 ( m o d 3 ) x ≡ 3 ( m o d 5 ) x ≡ 2 ( m o d 7 ) \begin{cases} x \equiv 2 \pmod{3} \\ x \equiv 3 \pmod{5} \\ x \equiv 2 \pmod{7} \end{cases} x2(mod3)x3(mod5)x2(mod7)

定理内容

m 1 , m 2 , … , m k m_1, m_2, \ldots, m_k m1,m2,,mk k k k两两互质的正整数,对于任意的整数 a 1 , a 2 , … , a k a_1, a_2, \ldots, a_k a1,a2,,ak,同余式组
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋮ x ≡ a k ( m o d m k ) (1) \begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \vdots \\ x \equiv a_k \pmod{m_k} \tag{1} \end{cases} xa1(modm1)xa2(modm2)xak(modmk)(1)
有唯一解,解的形式为:
x ≡ M 1 ′ M 1 a 1 + M 2 ′ M 2 a 2 + … + M k ′ M k a k ( m o d M ) (2) x \equiv M_1' M_1 a_1 + M_2' M_2 a_2 + \ldots + M_k' M_k a_k \pmod{M} \tag{2} xM1M1a1+M2M2a2++MkMkak(modM)(2)
其中, M = m 1 × m 2 × … × m k M = m_1 \times m_2 \times \ldots \times m_k M=m1×m2××mk M i = M m i M_i = \frac{M}{m_i} Mi=miM M i ′ M i ≡ 1 ( m o d m i ) M_i' M_i \equiv 1 \pmod{m_i} MiMi1(modmi)(即 M i ′ M_i' Mi M i M_i Mi 关于模 m i m_i mi 的逆元)。

定理中有一个很强的限制条件就是这些 m i m_i mi需要两两互质,但是我们的题干中并没有这个条件,因此需要进行一些推导

我们先从将题目简化看是否能够理出思路,假设有以下方程组需要求解
{ x m o d    a 1 = m 1 x m o d    a 2 = m 2 (3) \begin{cases} x \mod a_1 = m_1 \\ x \mod a_2 = m_2 \\ \tag{3} \end{cases} {xmoda1=m1xmoda2=m2(3)
那么就有下面的等式:
{ x = k 1 ⋅ a 1 + m 1 x = k 2 ⋅ a 2 + m 2 (4) \begin{cases} x = k_1 \cdot a_1 + m_1 \\ x = k_2 \cdot a_2 + m_2 \\ \tag{4} \end{cases} {x=k1a1+m1x=k2a2+m2(4)
利用式(5)中的等式就可以得到下面的式子:
k 1 ⋅ a 1 + m 1 = k 2 ⋅ a 2 + m 2 (5) k_1 \cdot a_1 + m_1 = k2 \cdot a_2 + m_2 \tag{5} k1a1+m1=k2a2+m2(5)
移项可得:
k 1 ⋅ a 1 − k 2 ⋅ a 2 = m 2 − m 1 (6) k_1 \cdot a_1 - k_2 \cdot a_2 = m_2 - m_1 \tag{6} k1a1k2a2=m2m1(6)
这里就是我们之前用扩展欧几里得算法求解的方程了,上式有解需要满足 m 2 − m 1 m_2 - m_1 m2m1能够整除 gcd ⁡ ( a 1 , a 2 ) \gcd(a_1,a_2) gcd(a1,a2),求出 k 1 , k 2 k_1,k_2 k1,k2后即可得 x x x的值。

那么这里式(6)就可以看成是一个一次不定方程,我们假设其有解,即上述有解条件成立,那么就可以使用扩展欧几里得算法找到一组特解 ( k 1 ′ , k 2 ′ ) (k_1',k_2') (k1,k2),其通解形式就可以表示为
{ k 1 = k 1 ′ + k a 2 g c d ( a 1 , a 2 ) k 2 = k 2 ′ + k a 1 g c d ( a 1 , a 2 ) (7) \begin{cases} k_1 = k_1' + k\frac{a_2}{gcd(a_1,a_2)} \\ k_2 = k_2' + k\frac{a_1}{gcd(a_1,a_2)} \\ \tag{7} \end{cases} {k1=k1+kgcd(a1,a2)a2k2=k2+kgcd(a1,a2)a1(7)
那么再将上述结果带入式(5)即可得到 x x x的所有解的形式,这里将 gcd ⁡ ( a 1 , a 2 ) \gcd(a_1,a_2) gcd(a1,a2)记为 d d d
x = k 1 ⋅ a 1 + m 1 = ( k 1 ′ + k a 2 d ) a 1 + m 1 = a 1 k 1 + m 1 + k a 1 a 2 d = a 1 k 1 + m 1 + k ⋅ [ a 1 , a 2 ] \begin{equation} \begin{aligned} x & = k_1 \cdot a_1 + m_1 \\ & = (k_1' + k\frac{a_2}{d}) a_1+m_1 \\ & = a_1k_1 + m_1 + k \frac{a_1 a_2}{d} \\ & = a_1k_1 + m_1 + k \cdot [a_1,a_2] \\ \tag{8} \end{aligned} \end{equation} x=k1a1+m1=(k1+kda2)a1+m1=a1k1+m1+kda1a2=a1k1+m1+k[a1,a2](8)
这里将 a 1 k 1 + m 1 a_1k_1 + m_1 a1k1+m1记为新 x 0 x_0 x0,将 k ⋅ [ a 1 , a 2 ] k \cdot [a_1,a_2] k[a1,a2]记为新的 a a a,则有
x = x 0 + k a (9) x = x_0 + ka \tag{9} x=x0+ka(9)
也就是说我们可以将式(5)的两个不定等式变为式(10),每两个式(4)所示的方程可以缩减一次,那么 n − 1 n-1 n1次之后就可以只有一个方程了,而上面这个方程其实就是求一个最小正整数解 x x x满足,到这就可以开始写代码啦,代码中也给出了一些注释
x   m o d   a ≡ x 0 (10) x \bmod a \equiv x_0 \tag{10} xmodax0(10)

代码

#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;  // 需要long long,因为我们会求最多25个数的最小公倍数

LL exgcd(LL a, LL b, LL &x, LL &y)   // 扩展欧几里得的模版,
{
    if(!b)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}

int main()
{
    int n;
    cin >> n;
    
    LL a1, m1;   
    cin >> a1 >> m1;  
    
    bool flag = true;
    for(int i = 0; i < n - 1; i ++)  // 按照我们推导的过程,每两个方程进行一次合并
    {
        LL a2, m2;
        cin >> a2 >> m2;
        
        LL k1, k2;
        LL d = exgcd(a1, a2, k1, k2);
        
        if((m2 - m1) % d)   // 扩展欧几里得算法有解的条件,找一下上文中高亮部分
        {
            flag = false;
            break;
        }
        k1 *= (m2 - m1) / d;  // 这里还是扩展欧几里得的内容,我们算的等式右边是(m2-m1)的最大公约数,系数要乘回去
        LL t = a2 / d;
        k1 = (k1 % t + t) % t; // 这里的操作是取一个最小的k,防止爆int
        
        m1 = a1 * k1 + m1; // 这里就是上面的x0
        a1 = abs(a1 / d * a2); // 这里就是上面的[a1,a2]
    }
    
    if(flag)
    {
        cout << (m1 % a1 + a1) % a1 <<endl; // 这里的操作和上面对k1的是一样的,求一个最小正整数解,应该不难理解
    }
    else puts("-1");
    
    return 0;
}

http://www.kler.cn/a/508098.html

相关文章:

  • PCL 新增自定义点类型【2025最新版】
  • 企业邮箱iRedMail搭建
  • 【机器学习实战入门】使用 Pandas 和 OpenCV 进行颜色检测
  • 关于高级工程师的想法
  • 在Mac mini上实现本地话部署AI和知识库
  • 图数据库 | 18、高可用分布式设计(中)
  • GaussDB创建不同兼容模式的数据库
  • MMDetection学习系列(4)——Cascade R-CNN深度探索与实战指南
  • 进程的家园:探索 Linux 地址空间的奥秘
  • 多线程进阶-线程安全的集合类
  • 游戏如何检测Xposed框架
  • C#实例化类,当类名和方法一样或者不一样时运行流程
  • 【达梦数据库(Oracle模式)】如何将视图中的数据导出
  • Python 爬虫学习指南与资料分享
  • rsync结合inotify实现文件实时同步
  • Lua项目下SSRF利用Redis文件覆盖lua回显RCE
  • 人工智能之深度学习_[3] -PyTorch自动微分模块和构建线性回归模型
  • 1.1初探大模型:起源与发展
  • 如何将数据库字符集改为中文,让今后所有的数据库都支持中文
  • 二十三种设计模式-代理模式
  • IF=24.5! 综述:机器人纹理识别触觉感知和机器学习进展
  • 请求响应-
  • 【算法】差分
  • python爬取Boss直聘,分析北京招聘市场
  • Android-V lmkd 中的那些属性值
  • WORD转PDF脚本文件