这个问题源自于我国数学古书《孙子算经》中的一道问题:“今有物,不知其数,三三数之,剩二;五五数之,剩三;七七数之,剩二。问物几何?”意思是一个整数除以三余二,除以五余三,除以七余二,求这个整数(满足条件且最小)。
做法是:
n%3=2,n%5=3,n%7=2且3,5,7互质 找到使(5×7)的倍数模3得1的数,答案是70 找到使(3×7)的倍数模5得1的数,答案是21 找到使(3×5)的倍数模7得1的数,答案是15 那么(70×2+21×3+15×2) % (3×5×7) = 23,23就是最终答案了
理解如下:
70×2 = 140,当中的2是指n%3 = 2的2,因为70%3=1,乘2后使其能满足%3=2的条件,同理, 21×3使其能满足%5=3 15×2使其能满足%7=2 又70能整除5和7,21能整除3和7,15能整除3和5 因此70×2+21×3+15×2 = 233能满足%3=2,%5=3,%7=2的条件 将233对3×5×7=105取模,是为了取得最小的情况,因为105是能够同时整除3,5,7的数,如果减去,对该数满足%3=2,%5=3,%7=2没有影响。因此对105取模,得到的结果便是最小并满足条件的数
拓展开来问题就是有同余方程组 a = ai (mod ni), 求未知数a。方法是:
定义 n=n1*n2...nk, 其中因子两两互质 mi = n1*n2*...nk / ni; ci = mi(mf mod ni); 其中 mi*mf mod ni = 1; 则 a = (a1*c1+a2*c2+...+ak*ck) (mod n)
中国剩余定理关键是mf的求法,如果理解了扩展欧几里得 ax+by=d, 就可以想到:
mi*mf mod ni = 1 => mi*mf+ni*y=1;
这里的中国剩余定理必须要求除数是互质的,但是有些题目的同余方程式除数并不互质,那么将不能使用传统的中国剩余定理
问题描述:POJ 1006
人自出生起就有体力,情感和智力三个生理周期,分别为23,28和33天。一个周期内有一天为峰值,在这一天,人在对应的方面(体力,情感或智力)表现最好。通常这三个周期的峰值不会是同一天。现在给出三个日期,分别对应于体力,情感,智力出现峰值的日期。然后再给出一个起始日期,要求从这一天开始,算出最少再过多少天后三个峰值同时出现。
问题分析:
这一题的做法可以类比算经中问题的方法
找到使(23×28)的公倍数模33得1的数,答案是1288
找到使(23×33)的公倍数模28得1的数,答案是14421|
找到使(28×33)的公倍数模23得1的数,答案是5544
(5544×p+14421×e+1288×i) % (23×28×33) = ans + d
若使用中国剩余定理的一般情况下的处理,则可按照一般情况的方法写出程序,参考核心程序为:
int mod[3],res[3];//除数,余数 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 temp = x; x = y; y = temp-a/b*y; } int china_mod()//中国剩余定理 { int mul=1,ans=0,mf,y,mi; for (int i=0; i<3; i++) mul *= mod[i]; for (int i=0; i<3; i++){ mi = mul/mod[i]; exGCD(mi,mod[i],mf,y); ans += (mi*mf*res[i])%mul; } return (ans+mul)%mul; }
#include<iostream> #include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<set> #include<map> #include<cstring> #include<string> #include<vector> #define LL long long using namespace std; LL mod[3] = {23,28,33}; LL Ex_Gcd( LL a , LL b , LL &x ,LL &y ) { if( b == 0 ) { x = 1; y = 0; return a; } LL mod = Ex_Gcd( b , a % b , x , y ); LL temp = x; x = y; y = temp - (a/b)*y; return mod; } LL Solve( LL mul ,LL re[]) { LL ans = 0,x,y; for( int i = 0; i < 3 ; i ++ ) { LL mi = mul/mod[i]; Ex_Gcd( mi , mod[i] , x ,y ); ans +=(x*re[i]*mi )%mul; } return (ans+mul)%mul; } int main( ) { LL mul = 1; for( int i = 0 ; i < 3 ; i ++ ) mul *= mod[i]; LL re[3],d; int Case = 1; while( 1 ) { int cnt = 0; for( int i = 0 ; i < 3 ; i ++ ) { scanf( "%I64d",&re[i] ); if( re[i] == -1 ) cnt++; } scanf( "%I64d",&d ); if( d==-1&&cnt == 3 ) break; LL ans = Solve( mul ,re) -d; while( ans <= 0 ) ans += mul; printf( "Case %d: the next triple peak occurs in %I64d days.\n",Case++,ans ); } //system( "pause" ); return 0; }