niangniang~
韩信点兵的老问题啦~模线性方程~
韩信点兵
问题描述:
在《孙子算经》中有如下“韩信点兵”问题:
有兵一列,三三数之余二,五五数之余三,七七数之余二,问兵几何?
问题等价于求x使,
你的任务是从输入中读取若干组的数据,求解由n个同余式组成的同余方程的解。
输入:
输入有若干组数据。每组数据的第一行上有整数n,表示下面将有n个一次同余方程x≡bi(mod mi)组成一个同余方程组。接下来的t行的每一行上有2个整数bi和mi,(mi>0),它们之间用一个或几个空格隔开,表示一次同余方程x≡bi(mod mi)。现要求n个一次同余方程组成的同余方程组的解。
输出:
对每一组测试数据,每行输出该一次同余方程的小于m的最小非负解。
输入样例:
2
5 3
7 5
输出样例:
2
之前有在网络上看到个很特别的思路,就是先找个整数N=1,然后看他能否满足三三数之余二,五五数之余三,七七数之余二,不满足则加一,满足则程序结束,就是穷举法。当然不是不可以,但是遇到其他问题还是会有超时的危险,所以不是很建议。
我们还是走正统路线。这里介绍一下中国剩余定理。
中国剩余定理又称孙子定理,实话说之前有学数学的同志和我讨论过这个问题,当时我就是穷举来着。。。。后来发现显然是有更深的研究意义存在。
设m1,m2,...,mk是两两互素的正整数,即gcd(mi,mj) =1,i≠j,i,j = 1,2,...,k
则同余方程组:
x≡b1 (mod m1)
x≡b2 (mod m2)
...
x≡bk (mod mk)
模[m1,m2,...,mk]有唯一解,即在[m1,m2,...,mk]的意义下,存在唯一的x,满足:
x≡bi mod [m1,m2,...,mk],i = 1,2,...,k
这是最基本的数学解释,我们看看它的解的基本组成部分:
m=m1×m2×m3×m4×.....×mn
Mi=m/mi Mi'是Mi的逆,也就是Mi与mi在gcd(Mi,mi)=1时候的最小非负x解。这里我们可以用扩展的欧几里德算法算出一个特解x,再用r = mi/gcd(M, mI);x=(x% r + r) % r ;做处理,求出最小非负x,就是Mi.
最后解的形式:a=(∑Mi×Mi'×bi)%m
证明暂时没有找到合适的版本。
下面是代码,我把计算a的过程也打印出来了,供作参考。
#include<iostream> using namespace std; long HX(int m[],int b[]); int exGcd(int a, int b, int &x, int &y); int gcd(int a,int b); int n; int main() { cin>>n; int *b,*m; b=new int [n]; m=new int [n]; for(int i=0;i<n;i++) { cin>>b[i]>>m[i]; } HX(m,b); return 0; } int gcd(int a,int b) { if(a==0) { return b; }else { return gcd(b % a,a); } } long HX(int m0[],int b[]) { int x,y,m=1,a=0; int i; for(i=0;i<n;i++) m=m*m0[i]; for(int j=0;j<n;j++) { int MM=m/m0[j]; exGcd(MM,m0[j],x,y); int r = m0[j]/gcd(MM, m0[j]); x=(x% r + r) % r ; //要求的是最小非负 a=(a+MM*x*b[j]); cout<<MM<<" "<<x<<" "<<b[j]<<endl; //测试结果 } cout<<a%m<<endl; return 0; } int exGcd(int a, int b, int &x, int &y) { if(b==0) { x=1; y=0; return a; } int r=exGcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return r; }
完毕~