打卡第二十五天

模乘逆元与孙子定理

孙子定理也称为中国剩余定理。

《孙子算经》卷下第二十六题(“物不知数”问题):有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

孙子定理讲的是求解一元线性同余方程组的方法。


有人指出,孙子定理有以下5种解法:

1.枚举法
2.解不定方程法
3.逐级满足法
4.化为相同除数的同余式法
5.经典同余式方程组解法

据说最为简洁快速的是第4种方法。

这里介绍的程序为第5种解法。

要得到解x,首先需要计算模乘逆元。模乘逆元定义为:

满足 ab≡1(mod m),称b为a模乘逆元。

程序中给出了两种求模乘逆元的方法,一种是穷举法,此法对于模值较大时是一种低效率的方法;另外一种是利用扩展欧几里得算法求逆元。

求得模乘逆元之后,就可以算出同余方程组的解。

“物不知数”问题的一个解为23(最小值解),其解系为x=23+105k(k>=0)(105=3*5*7)。

#include<stdio.h>
// 递推法实现扩展欧几里德算法
long exgcd(long a,long b,long *x,long *y)
{
	long x0=1,y0=0,x1=0,y1=1;
	long r,q;
	*x=0;
	*y=1;
	r=a%b;
	q=(a-r)/b;
	while(r)
	{
		*x=x0-q*x1;
		*y=y0-q*y1;
		x0=x1;
		y0=y1;
		x1=*x;
		y1=*y;
		a=b;
		b=r;
		r=a%b;
		q=(a-r)/b;
	}
	return b;
	
 } 
// 扩展欧几里德算法求逆元
 long minv(long a,long p)
 {
 	long x,y;
 	exgcd(a,p,&x,&y);
 	return x<0?x+p:x;
 }
// 试探法求逆元
 long minv2(long a,long p)
 {
 	long y=1,t;
 	int i;
 	if(a<0){
 		a=a%p;
 		a+=p;
	 }
	 for(i=1;i<p;i++){
	 	t=a*i;
	 	if(t%p==1){
	 		y=i;
	 		return y;
		 }
	 }
	 return y;
 }
 int main(void)
 {
 	printf("a=%d m=%d x=%ld x=%ld\n",65,83,minv(65,83),minv2(65,83));
 	printf("a=%d m=%d x=%ld x=%ld\n",11663,103,minv(11663,103),minv2(11663,103));
 	printf("a=%d m=%d x=%ld x=%ld\n",11227,107,minv(11227,107),minv2(11227,107));
 	
 	printf("a=%d m=%d x=%ld x=%ld\n",11021,109,minv(11021,109),minv2(11021,109));
 	long a[]={2,3,2};
 	long m[]={3,5,7};
 	int i,size=sizeof(a)/sizeof(long);
 	long bm=1,subm[size],x=0;
 	for(i=0;i<size;i++)
 	bm*=m[i];
 	for(i=0;i<size;i++)
 	subm[i]=bm/m[i];
 	for(i=0;i<size;i++){
 		x+=subm[i]*minv(subm[i],m[i]*a[i]);
 		x%=bm;
	 }
	 printf("x=%ld\n",x);
	 return 0;
 	
 	
 }

 

结果:

猜你喜欢

转载自blog.csdn.net/huangluping12345/article/details/83187216