解题报告 (四) 中国剩余定理

本文详细介绍了中国剩余定理的概念及应用,通过多个实例解析算法原理,并提供了多种问题的解决方案,包括最小非负整数解的求解方法、置换群的应用、以及与Lucas定理结合解决复杂问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中国剩余定理

中国剩余定理,求的是模线性方程组的通解,如图1所示,其中(mi, ai)都是已知量,x是未知量。需要求的就是x,让它满足以下n个等式:

图1

举个最简单的例子,如图2所示,x满足三个同余方程,我们可以将三个方程进行一个转化。

图2

很明显,图2和图3的方程组等价,图3表示的是4个未知数,3个方程。由于未知数个数大于方程数,所以这个方程组要么无解,要么有无穷多解。

图3

 

朴素的做法就是枚举x,然后对每个方程进行试除,令x=(0,1,2,3,...),那么我们发现当x=26时可以满足所有三个方程。然而事情并没有那么简单,除数的不同,或者方程数的增多,都可能导致x的值变得很大。

如图4所示的这种情况,仅仅三个方程,x就已经是10^8的数量级了,所以枚举这种做法显然是行不通的。

图4

 

现在介绍一种O(N)的算法,其中N为方程组的个数。首先,我们需要定义一些向量,向量a[]代表除数集合,向量m[]代表余数集合,向量x[]则表示未知数集合。如图5所示,其中K为需要求的数,我们将n个方程从0开始按顺序编号。

图5

 

然后,我们合并(0)和(1)两个等式,得到:

图6

代换后A,B,C均为常数,X,Y为未知数,然后求A和B的最大公约数G,如果G不能整除C,则方程组无解;否则方程两边A,B,C同时除以G,等式不变。接着,就可以通过扩展欧几里得算法求解Y了,需要注意的是,扩展欧几里得求解时需要保证A和B都大于等于零,所以我们可以做如下处理:

        if(A < 0) {
                // 将A转化成正数,但还需保证等式性质,所以A,B,C同时变号
        	A = -A, B = -B, C = -C;
                // 将B转化成正数,C++的取模%可能出现负数,所以需要模完后加上模数再取模
        	B = (B % A + A) % A;
	}

扩展欧几里得算法求解二元一次方程的解法可以参考这篇文章:初等数论基础算法

         Y的通解表示如下(其中Y0已经求出为常数):

图7

Y就是(1)方程中的x[1],我们将Y代入方程(1),得到等式如下:

图8

 

这时候,我们发现两个等式合并以后的等式,仍然可以表示成同余方程的形式。这说明可以将这个等式和(2)继续合并,合并完的等式,继续和(3)合并,直到最后只剩一个等式,令最后这个等式为:K=ax+b,由于x可以取任意整数,所以我们可以保证a和b都大于等于0,并且a >= b,那么这时候K=b就是方程组的最小非负整数解,K=ax+b就是方程组的通解。

这就是中国剩余定理的O(N)解法。

最后来看实际算法流程:

0)枚举 i = 1 to n-1

1) 合并等式(0)和(i), a[0]*x[0] + m[0]= a[i]*x[i] + m[i];

a[0]*x[0] - a[i]*x[i] = m[i] - m[0];

A * X + B * Y = C;

2) 根据扩展欧几里得,求得x[i] = Y = Y0 + A*t; (t为任意整数)

3) 将x[i]代入等式(i),得到: K = a[0]*a[i]*t + (m[i]+a[i]*Y0);

4) 更新等式(0), a[0] = a[0]*a[i]

                           m[0] = (m[i]+a[i]*Y0) % a[0];

5) n-1次迭代完毕,m[0]就是最小非负整数解。

C++代码实现地址:中国剩余定理O(N)算法

中国剩余定理题集解题报告

1Strange Way to Express Integers

题意:给定N组数(ai, ri)。求一个最小的非负整数K,满足所有的K mod ai = ri。

题解:中国剩余定理的模板题。

 

2Biorhythms

题意:慢慢读题吧,读到最后就是N=3的中国剩余定理。

题解:直接上模板。

 

3X问题

题意:给定N和n组数(ai, ri),求满足所有的X mod ai = ri,且X<=N的正整数X的个数。

题解:首先套模板求出最小解x,并且求LCM = ai的最小公倍数;然后分情况:

1) x>N 或 无解,输出0;(我的模板无解返回的是-1,结果因为这个WA了很多次,无语)

2)x=0,输出N/LCM

3)x>0, 输出(N-x)/LCM+1

4Circle

题意:给定长度为n(n <= 20)的约瑟夫环,按顺序报数,每次报到k的那个人出列;然后继续从下一个人报数,每次报到k的那个人出列。现在给定出队序列,求最小的满足条件的k。

题解:hash+中国剩余定理。出队序列已经给出,那么可以按顺序模拟,每次往环上走一步,如果遇到本次出队的人,将出队的人hash掉,记录上一次到本次的步数。以剩余的人个数为模数,步数为余数建立一个方程。N次建立N个方程然后求一次中国剩余即可。

5Chinese Remainder Theorem Again

题意:给定数组Mi和数字a,求一个最小的数K,满足K mod Mi = (Mi - a)。

题解:看起来赤裸裸的中国剩余定理,如果直接套模板会WA,因为数据范围的问题,可能会超int64,所以由于这题的特殊性,有更简单的解法。还是按照中国剩余定理的思路,合并等式发现,最后的解其实是所有Mi的最小公倍数再减去a。

6AndNow,a Remainder from Our Sponsor

题意:按照同余来加解密的题,有点意思。

题解:模拟 + 中国剩余定理。

 

7HelloKiki

题意:给定N组数(ai, ri)。求一个最小的正整数K,满足所有的K mod ai = ri。

题解:中国剩余定理的模板题。注意最小正整数,如果结果为0,需要输出ai的最小公倍数。

 

8A Poor Officer

题意:N(N <= 10000)个伞兵编号递增排列(即12345...),给定一种重排规则A[N],A[i]表示将第A[i]个伞兵放到第i个位置(1<=i<=N)。然后给定一个目标状态,问从起始状态到达目标状态,最少需要经过多少步。

图9

 

题解:置换群 + 中国剩余定理。首先,对于任意的重排序列,经过有限次重排后必然回到初始状态,如图10所示,重排数组A=[2,1,4,5,7,6,3];目标数组T=[2,1,7,3,4,6,5]。

图10

 

然后对起始状态和目标状态分别进行一次重排,就能算出各自的置换群了,如果两者的置换群不一致,显然状态不可达,直接输出-1即可;如图11所示,两者的置换群是一致的,因为(3457)可以通过循环左移经过(4573)、(5734)变成(7345),同理,(12)可以经过一步变成(21),所以两者置换群一致。

图11

 

然后,计算起始状态和目标状态的置换群中每个循环可达需要的最少步数,例如(12)到(21)需要1步,(3457)到(7345)需要3步,(6)到(6)需要0步;然后就可以列出三个方程,即总步数K满足:

图12

 

成功转换成中国剩余定理求解。

9Shuffling

解法同A Poor Officer

10Mysterious For

题意:给定n(n <= 10^6),表示循环最多执行n次,再给定两种类型的循环,定义如下:

现在总共有m(m <= 10^5)个嵌套,【类型1】的循环不大于16个。问所有嵌套的循环一共执行多少次。由于次数很大, 需要将答案模上364875103。

 

题解:Lucas定理 + 中国剩余定理。

首先,以【类型1】为断点,如果a[i]是【类型1】的循环,那么它和a[i-1]必然没关系。图中XX()的执行次数代表一个【类型1】循环嵌套了两个【类型2】的循环。

我们用S(n,t)表示1个【类型1】循环和t个【类型2】循环的总执行次数。那么S(n,0)=n, S(n,1)=1+2+...+n。利用数学归纳法,我们总结出S(n,t)的递推公式:S(n,t)=S(n-1,t)+S(n,t-1)。

然后将n和t记录到表格中,每个格子的值 = 它上面格子的值 + 它左边格子的值,如图所示:

 

观察可得,它是一个斜着的杨辉三角,并且可以转化成组合数求解:S(n,t)=C(n+t,t+1)。于是转变成了求组合数C(a,b) mod 364875103。

C(a,b) mod 364875103的求法采用Lucas定理 + 中国剩余定理。由于364875103 = 97 * 3761599,为两个素数的乘积,我们可以先求出x = C(a,b) mod 97,和y = C(a, b) mod 3761599,那么令K = C(a,b) mod 364875103,K必然满足:

如果x和y都是已知的,那么就可以通过中国剩余定理来求K了。

组合数C(a,b)%p(其中p为素数)可以采用Lucas定理求解。Lucas定理的递归形式如下:

Lucas(n, m, p) {
	if(m == 0) return 1;
	return Lucas(n/p, m/p, p) * Comb(n%p, m%p, p) % p;
}

其中Comb(n,m,p)表示求C(n,m)%p (n<p && m<p && p为素数)

那么现在就是要求n! mod p的值,其中n的范围<=1100000,直接预处理存到数组中,O(1)获取即可。n!^(p-2)mod p可以采用二分快速幂取模。

 

11The Lastest Math Theory Problem

最后推荐一道我出的题^_^

题意:给定n(n <= 6)和数组a[n](a[i] < 10000),要求求一个最小的N,满足n个等式(其中x[i]为未知量):

由于N可能很大,所以只需要输出N mod 19880502即可;如果不存在这样的N,则输出-1。

题解:中国剩余定理 + 二分快速幂。任何数都可以表示成素数的乘积,又由于N满足以上六个等式,所以N的素因子一定是a[i]的素因子,假设N的表示如下:

任何一个p[i]必定小于10000,那么我们只需要枚举所有小于10000的素因子,然后想办法求出指数e[i]即可。对于每个小于10000的素数p[i],枚举所有a[j], 检查a[j]这个数中有多少p的素因子,记为cnt[j],则e[i] = k*a[j] + cnt[j](k>=0);那么对于每个e[i]利用中国剩余定理求n个同余方程,得到的e[i]利用二分快速幂求p[i]^e[i] mod 19880502即可。最后所有素数分量连乘再取模就是最后的答案了。

这题需要注意一点,求中国剩余定理的时候,由于值的范围在int64,所以乘法可能溢出。所以如果两个数相乘需要采用二进制法。这里提供一种非递归版本的a*b mod c,全程只有加法和取模。

LL Product_Mod(LL a, LL b, LL c) {
	LL sum = 0;
	while(b) {
		if(b & 1) sum = (sum + a) % c;
		a = (a + a) % c;
		b >>= 1;
	}
	return (sum + c) % c;
}
<think>好的,我现在需要解决用户的问题,关于中国剩余定理在Python中的实现,特别是与PTA平台相关的解题思路。首先,我要回忆中国剩余定理的基本概念和应用场景。中国剩余定理,又称孙子定理,用于解决一组同余方程的问题,找到满足所有方程的最小正整数解。这在数论和编程题中比较常见。 用户提到了PTA,也就是浙江大学的编程练习平台,可能他们遇到了相关的题目,需要具体的实现方法。根据用户提供的引用,特别是引用[1]和[2],里面有Python实现的例子,可以结合这些资料来构建回答。 首先,我需要明确中国剩余定理的数学原理。定理要求模数两两互质,这样才能保证解的存在唯一性。然后,解题步骤通常分为三步:计算所有模数的乘积M,计算每个Mi=M/mi,找到Mi的模mi逆元,最后用这些值求和取模得到解。 接下来,我需要将这些步骤转化为Python代码。例如,如何实现扩展欧几里得算法来求逆元,如何处理多个同余方程的情况,以及如何处理可能的错误情况,比如模数不互质导致无解的情况。 考虑到PTA题目可能有时间限制或特定的输入输出要求,代码需要高效且能够处理大数。因此,在代码中需要注意算法的优化,比如使用迭代而非递归的扩展欧几里得算法,避免栈溢出,并且处理输入输出的方式要符合题目要求。 然后,我需要考虑用户可能的疑问,比如如何处理模数不互质的情况,这时中国剩余定理是否适用,或者如何调整算法。此外,可能需要提供测试用例或示例,帮助用户理解代码的正确性。 最后,根据用户的要求生成相关问题,这些问题应该围绕中国剩余定理的应用、优化、错误处理等方面,帮助用户进一步深入学习。</think>### 中国剩余定理的Python实现与PTA解题思路 #### 一、中国剩余定理原理 中国剩余定理用于求解形如下列同余方程组的最小正整数解: $$ \begin{cases} x \equiv a_1 \pmod{m_1} \\ x \equiv a_2 \pmod{m_2} \\ \vdots \\ x \equiv a_n \pmod{m_n} \end{cases} $$ **前提条件**:模数$m_1, m_2, \dots, m_n$两两互质[^2]。 #### 二、Python实现步骤 1. **计算所有模数的乘积**: $$M = \prod_{i=1}^n m_i$$ 2. **计算部分乘积**: $$M_i = \frac{M}{m_i}$$ 3. **求乘法逆元**: 通过扩展欧几里得算法求$M_i^{-1} \pmod{m_i}$ 4. **构造解**: $$x = \sum_{i=1}^n a_i M_i M_i^{-1} \mod M$$ #### 三、Python代码实现 ```python def exgcd(a, b): if b == 0: return a, 1, 0 gcd, x, y = exgcd(b, a % b) return gcd, y, x - (a // b) * y def crt(equations): """ equations: 包含(a_i, m_i)的列表,如 [(2,3), (3,5), (2,7)] 返回: 最小正整数解x """ M = 1 for a, m in equations: M *= m total = 0 for a_i, m_i in equations: Mi = M // m_i gcd, inv, _ = exgcd(Mi, m_i) if gcd != 1: raise ValueError("模数不互质,无解") total += a_i * Mi * inv return total % M ``` #### 、PTA解题要点 1. **输入处理**: - 注意题目输入格式(多组测试数据/单组数据) - 典型输入形式示例: ``` 3 2 3 3 5 2 7 ``` 2. **边界条件处理**: - 检查模数是否互质 - 处理$a_i=0$的特殊情况 - 结果范围控制(通常要求最小正整数解) 3. **性能优化**: - 使用迭代实现扩展欧几里得算法(避免递归深度限制) - 预计算模数乘积的分解质因数 #### 五、典型错误处理 ```python # 模数不互质时的处理(增强鲁棒性) try: print(crt([(2,4), (3,6)])) except ValueError as e: print("No solution") ``` #### 六、测试用例 | 输入 | 期望输出 | |-------------------|--------| | [(2,3), (3,5), (2,7)] | 23 | | [(1,5), (2,7)] | 16 | | [(0,2), (0,3)] | 0 | [^1]: 中国剩余定理及Python实现参考 [^2]: 《孙子算经》问题原型与应用背景
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值