中国剩余定理
中国剩余定理又叫孙子定理。在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之 剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,解决的孙子问题的一般解法就叫孙子定理又叫 天朝 中国剩余定理。
具体解法分两步:
1.找出3和5公倍数中除7余2的最小的数(30)、3和7公倍数中除5余3的最小的数(63)、5和7公倍数中除3余2的最小的数(140),然后将这三个数相加得233。(ps:为了简化运算第一个数可以找 % 7 = 1的数之后在乘2,其他两个数同理分别乘3、2。)
而寻找取模后为1的数即为寻找其逆元,逆元的概念后面会说。
2.用233除以3、5、7的最小公倍数105,得到余数23(即233%105 = 23),23即为所求。
是不是一脸懵(如果明明白白的请无视我这个菜鸡),我第一次看也是想说就这么简单???
但这是有数学依据的:
设这三个数分别为n1、n2、n3。因为n1 % 7 == 2,n2、n3又都是7的倍数所以,(n1 + n2 + n3) % 7 == 2,其他两个同理,所以最后得到的数233(n1+n2+n3)就满足孙子问题中的三个条件,但这不一定是最小的解。
那么如何得到最小解?
只需要在该解的基础上最大限度的减去3、5、7的公倍数即可(即对该解取模233%105),为什么?因为这样无论怎么减都不会影响其对于3、5、7的模数。
逆元
给出 a 和 m ,一个数有逆元的充分必要条件是gcd(a,m)=1,此时逆元唯一存在,这时方程 ax ≡ 1(mod m)的最小整数解 x 称为 a 模 m 的逆元。
逆元的含义:
在模m意义下,一个数a如果有逆元x,那么除以a相当于乘x。
为什么要有乘法逆元呢?
当我们要求(a/b) mod p的值,且a很大,大到会溢出;或者说b很大,达到会爆精度。无法直接求得a/b的值时,我们就要用到乘法逆元。
那么如何求解逆元呢?
根据上文我们知道ax ≡ 1(mod m)有解的条件是a和m互素,即gcd(a,m) == 1.
易知ax mod m = ax - (ax / m) * m
所以ax ≡ 1(mod m) 等价于 ax - (ax / m) * m = 1
提出m得
ax + m( - ax / m) = 1
令y = -(ax/m)得
ax + my = 1
所以求解 ax ≡ 1(mod m)等价于求解 ax + my = 1 ,那么就可以用扩展欧几里得定理求解。
扩展欧几里得定理(exgcd):
给出整数a,b,n,求方程 ax + by = n 的所有整数解。有解的充分必要条件是gcd(a,b)可以整除 n 。简单解释如下:
令 a = gce(a,b) * a’、 b = gcd(a,b) * b’ , 则有 ax + by = gcd(a,b)(a’x + b’y) = n;如果 x 、y 、a’ 、b’都是整数,那么n必须是gcd(a,b)的倍数才有整数解。
exgcd解出的 x 为 ax + by = gcd(a,b) 中的 x ,若要求 ax + by = n中的 x,两边还要乘上 n / gcd(a,b)。
由欧几里得算法,得
ax+by=gcd(a,b)=gcd(b,a mod b)=bx′+(a mod b)y′
代入上文的a mod b = a - ⌊ a / b ⌋ b 得
ax + by = bx’ + (a - ⌊ a / b ⌋ b) y’
= bx’ + ay’ - ⌊ a / b ⌋ by’
= ay’ + b(x’ - ⌊ a / b ⌋y’)
得 x = y′ , y = x′ − ⌊ a / b ⌋y′
边界情况分析,ax′+by′=gcd(a,b),当 b=0 时,a 为 gcd(a,b),当且仅当 x′=1时等式成立,y′ 可以为任意值,为方便起见,设y′=0,那么得到以下代码:
void exgcd(int a,int b,int &x,int &y)
{
if(b==0){x=1;y=0;return;}
exgcd(b,a%b,x,y);
int z = x; x = y; y = z - (a/b)*y;
}
求出的 x 即为 a 模 b 的逆元。
介绍完了看看这道例题:
题目描述
自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有 1616 头母猪,如果建了 33 个猪圈,剩下 11 头猪就没有地方安家了。如果建造了 55 个猪圈,但是仍然有 11 头猪没有地方去,然后如果建造了 77 个猪圈,还有 22 头没有地方去。你作为曹总的私人秘书理所当然要将准确的猪数报给曹总,你该怎么办?
输入格式
第一行包含一个整数 n (建立猪圈的次数),接下来 n 行,每行两个整数 ai,bi, 表示建立了 ai个猪圈,有 bi 头猪没有去处。你可以假定 ai,aj互质。
输出格式
输出包含一个正整数,即为曹冲至少养母猪的数目。
输入
3
3 1
5 1
7 2
输出
16
#include<cstdio>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
ll n,a[15],b[15],c[15],mul = 1,ans;
void exgcd(ll a,ll b,ll &x, ll&y) {
if(b == 0) {x = 1;y = 0;return;}
exgcd(b,a%b,x,y);
int z = x; x = y; y = z-y*(a/b);
}
int main( ) {
scanf("%d",&n);
for(int i = 1; i <= n; i++) {
scanf("%d%d",&a[i],&b[i]);
mul *= a[i];//求出所有数的最小公倍数(因为每个数都互质)
}
for(int i = 1; i <= n; i++) {
c[i] = mul / a[i]; //除去a[i]所有数的最小公倍数
ll x = 0, y = 0;
exgcd(c[i],a[i],x,y);//求逆元
ans += b[i] * c[i] * (x < 0 ? x + a[i]: x);//累加成其中一个解
}
printf("%lld",ans%mul);//输出最小解
return 0;
}
两年没写博客了,如有错误欢迎斧正。