同余问题

博客介绍了同余的性质,回顾欧几里得算法并推导扩展欧几里得算法,阐述其性质。还介绍了线性同余方程的定义、性质和解法,包括最小整数解的求法。此外,讲解了一元线性同余方程组的求解方法,最后提及高次同余方程组、中国剩余定理及求最小负整数的模板。

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

性质: 

1.反身性:a≡a (mod m);
2.对称性:若a≡b(mod m),则b≡a (mod m);
3.传递性:若a≡b(mod m),b≡c(mod m),则a≡c(mod m);
4.同余式相加:若a≡b(mod m),c≡d(mod m),则a c≡b d(mod m);
5.同余式相乘:若a≡b(mod m),c≡d(mod m),则ac≡bd(mod m)。
 

先回顾一下欧几里得算法

gcd(a,b)=gcd(b,a%b)

int gcd(int a,int b)   //最大公约数
{
    if(b==0)  return a;
    else return gcd(b,a%b);
}

         
int lcm(int a,int b)  //最小公倍数
{
    return a/gcd(a,b)*b;    //防止溢出
}

扩展欧几里得算法

定义:ax+by=gcd(a,b)=d,在已知a,b的情况下,求解出一组x,y

推导过程:因为a%b=a-(a/b)b,则有 d=bx1+[a-(a/b)b]y1=bx1+ay1-(a/b)by1=ay1+b(x1-a/by1),故 x=y1,y=x1-a/by1

性质

1.若通过扩展欧几里得求出一组特解(x0,y0),那么有ax0+by0=d.
      则方程的通解为,其中k为任意整数
      x=x0+k*(b/d)
      y=y0-k*(a/d)
2.已知ax+by=d的解,对于ax+by=c的解,c为任意正整数,只有当d|c时才有解
    其通解为
    x=(c/d)x0+k(b/d)
    y=(c/d)y0-k(a/d)
 

void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		d=a;
		return ;
	}
	else{
		exgcd(b,a%b,d,x,y);
		ll temp=x;
		x=y;
		y=temp-(a/b)*y;
	}
}

线性同余方程

定义:形如:ax≡b (mod n)的方程。

性质

定理1:此方程对于未知量x有解当且仅当 gcd(a,n) | b
定理2:d=gcd(a,n),若d|b,则方程恰好有d个模n不同余的解,否则方程无解
定理3:若x0是方程的任一解,则该方程对模n有d个不同的解,分别为xi=x0+k*(b/d),(k=1,2,…,d-1)

解线性同余方程

ax≡b (mod n) —> ax+ny=b d=gcd(a,n),若不满足d|b,则方程无解 否则, ax0+ny0=d,利用扩展欧几里得求出一组特解(x0,y0) 然后,x=x0*(b/d)%n就是原方程的一个解 且其有d个不同的解,为xi=(x+k*(n/d))%n,0<=k< d

int f(int a,int b,int n){
	exgcd(a,n,d,x,y);
	if(b%d)
	return -1;//代表无解
	x=x*(b/d)%n;
	for(int i=1;i<=d;i++){
		ans[i]=(x+(i-1)*(n/d))%n;
	} 
} 

最小整数解

由于一元线性同余方程的通解可以写成res = ( X + i * (b /d) ) (mod n) = X + i * (n/d) + n * y,由于 y 与 i 均为变量因此可以将其合并得到式子 res = X + y * ( n/d) (其中将原式中的 n * y 看做 n/d * d * y,由于y是变量因此可以将 d*y这个整体看为 y),因此可以得到res = X(mod n/d) ,设m/d 为 t ,其最小正整数解可表示为 (X%t + t) % t。
 

int ex_gcd(int a,int b,int &x,int &y){
	if(b == 0){
		x = 1;y = 0;
		return a;
	}else{
		int r = ex_gcd(b,a%b,y,x);
		int t = y;
		y = x - (a/b)*y;
		x = t;
		return r;
	}
}
void RemainderEquation(int a,int b,int n)
{
    int X,Y,d;
    long long res;
    long long min_res;
    d=gcd(a,n);
    exgcd(a,n,X,Y);
    if(b%d == 0)
    {
        X = X * (b / d) % n;//得到同余方程一解
        for(int i = 0 ; i < d; i++)
        {
            res = (X + (i * (b/d))) % n;
            printf("%lld\n",res);             //输出所有解
        }
        min_res=(X%(n/d)+(n/d))%(n/d);    
        cout<<min_res<<endl;       //输出最小解
    }else
    {
        printf("No Sulutions!\n");
    }
}

解一元线性同余方程组

先以两个方程为例:

                                x\equiv r_{1}\left ( mod a_{1} \right )----------------------------------(1)

                                x\equiv r_{2}\left ( mod a_{2} \right )----------------------------------(2)

令a=(a_{1},a_{2}).

首先此方程组有解的充分必要条件是(a_{1},a_{2}) | (r_{1}-r_{2}),此时方程组仅有一个小于a的非负整数解,利用扩展欧几里得算法解出来。

式子(1)等价于  x=r_{1}+a_{1}y_{1} 

式子(2)等价于  x=r_{2}+a_{2}y_{2}

联立可得:  a_{2}y_{2}-a_{1}y_{1}=r_{1}-r_{2}

再根据之前解一元线性同余方程的方法,很容易得到这个方程的解,因此小于a的·非负整数解即为    \left ( r_{2}+a_{2}y_{2})%a \right )%a    。

那么不管这样的方程有多少个,都可以两两解决,求得方程组的最终解。

模板代码:

int solve(){
	cin>>a1>>r1;
	
	for(int i=1;i<n;i++){
		cin>>a2>>r2;
		ll a=a1,b=a2,c=r2-r1;
		exgcd(a,b,d,x0,y0);
		if(c%d!=0)
		{
			r1=-1;
			break;
		}
		ll t=b/d;
		x0=(x0*(c/d)%t+t)%t;
		r1=a1*x0+r1;
		a1=a1*(a2/d);
	}
	return r1;
}

附上其他博主的具体证明:

 解高次同余方程组

模板代码:

/*
解高次同余方程

A^x ≡ B( mod C )

c  是素数
时间复杂度O(sqrt(n)*logn)
POJ 2471

*/

#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
//快速幂求a^b
LL pow_mod(LL a,LL b,LL n)
{
    LL s=1;
    while(b)
    {
        if(b&1)
            s=(s*a)%n;
        a=(a*a)%n;
        b=b>>1;
    }
    return s;
}
//求解模方程a^x=b(mod n)。n为素数,无解返回-1
//费马小定理a^(n-1)=1(mod n),n为素数。a^0=1,所以循环节小于等于n,即如果存在解,则最小解x<=n
LL log_mod (LL a,LL b,LL n)
{
    LL m,v,e=1,i;
    m=ceil(sqrt(n+0.5));     //x=i*m+j
    //v=inv(pow_mod(a,m,n),n);  //a^m*v=1(mod n)
    v=pow_mod(a,n-m-1,n);
    map<LL,LL>x;
    x[1]=m;
    for(i=1;i<m;i++)  //建哈希表,保存x^0,x^1,...,x^m-1
    {
        e=(e*a)%n;
        if(!x[e])x[e]=i;
    }
    for(i=0;i<m;i++)//每次增加m次方,遍历所有1<=x<=n
    {
        if(x[b])
        {
            LL num=x[b];
            x.clear();//需要清空,不然会爆内存
            return i*m+(num==m?0:num);
        }
        b=(b*v)%n;   //b=b/(a^m)
    }

    return -1;
}
int main()
{
    LL a,b,n;
    while(scanf("%I64d%I64d%I64d",&n,&a,&b)!=EOF)
    {
        LL ans=log_mod(a,b,n);
        if(ans==-1)printf("no solution\n");
        else printf("%I64d\n",ans);
    }
    return 0;
}

中国剩余定理:https://blog.youkuaiyun.com/qq_41515833/article/details/86624537

求此类同余方程组最小负整数的模板:

ll China(ll r){
	M=1;
	ll i,Mi,x0,y0,d,ans=0;
	for(int i=1;i<=r;i++){
		M*=m[i];
	}
	for(int i=1;i<=r;i++){
		Mi=M/m[i];
		exgcd(Mi,m[i],d,x0,y0);
		ans=(ans+Mi*x0*a[i])%M;
	}
	if(ans<0){
		ans+=M;
	}
	return ans;
}

也可以用解线性同余方程组的方法求解:

ac代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll m[10],a[10],M;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y){
	if(b==0){
		x=1;y=0;
		d=a;
		return ;
	}
	else{
		exgcd(b,a%b,d,x,y);
		ll temp=x;
		x=y;
		y=temp-(a/b)*y;
	}
}
ll China(ll r){
	M=1;
	ll i,Mi,x0,y0,d,ans=0;
	for(int i=1;i<=r;i++){
		M*=m[i];
	}
	for(int i=1;i<=r;i++){
		Mi=M/m[i];
		exgcd(Mi,m[i],d,x0,y0);
		ans=(ans+Mi*x0*a[i])%M;
	}
	if(ans<0){
		ans+=M;
	}
	return ans;
}
int main(){
	ll t=0,p,e,i,d,temp;
	while(cin>>p>>e>>i>>d){
		if(p==-1&&e==-1&&i==-1&&d==-1){
			break;
		}
		t++;
		a[1]=p,a[2]=e,a[3]=i;
		m[1]=23,m[2]=28,m[3]=33;
		ll ans=China(3);
		while(ans<=d){
			ans+=M;
		}
		cout<<"Case "<<t<<":the next triple peak occurs in "<<ans-d<<" days."<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值