关于同余的问题

关于同余的问题本文主要介绍了同余方程的几种解决方法 包括利用欧拉定理和费马小定理解指数型同余方程 使用 exgcd 算法解线性同余方程 以及借助中国剩余定理和其扩展形式解决多个同余方程组

大家好,欢迎来到IT知识分享网。

1.欧拉定理及其推论与特殊情况(费马小定理)

欧拉定理:对于任意两个互质的数a,n,有a^{\varphi \left ( n \right )}\equiv 1\left ( mod \left ( n \right )\right ),可以通过简化剩余系的方法证明。

欧拉定理的推论:对于任意两个互质的数a,n,有a^{b}\equiv a^{\left ( b \right )mod\left ( \varphi \left ( n \right ) \right )}\left ( mod\left ( n \right ) \right )(其中b为任意整数)。特别地,当a,n是任意的两个数,且关于同余的问题\varphi \left ( n \right )”>时,有a^{b}\equiv a^{\left ( b \right )mod\left ( \varphi \left ( n \right ) \right )+\varphi \left ( n \right )}\left ( mod\left ( n \right ) \right )

费马小定理:若p为质数,有a^{p-1}\equiv1\left ( mod\left ( p \right ) \right )

有了这3个公式,就可以解决一些幂形式的同余方程了。

例题:AcWing 202.最幸运的数字(求解指数型同余方程)

类型1:求解特殊的指数型方程(可用欧拉定理)

本题较为困难,要求我们对于欧拉定理有足够深刻的理解并且要有极强的数论处理技巧。首先很容易想到把888……88这样形式的数转化为:\frac{8\left ( 10^{x}-1 \right )}{9},那么关键怎么求出x的值呢?

引理:

1.整除的重要性质1:若a\mid bc\neq 0,则有ac\mid bc

2.整除的重要性质2:若a,c两数互质,且c\mid ab,则有c\mid b

那么接下来问题就不再困难:利用这两个性质可以将式子最终化成:

10^{x}\equiv 1\left ( mod\left ( \frac{9L}{d} \right ) \right )

那么这类未知数在指数上的同余方程要怎么解呢?当然要联系欧拉定理啦!我们猜想x是\varphi \left ( x \right )的因数。这很容易证明出来。所以我们只要枚举\varphi \left ( x \right )的因数并一个一个验证就可以了。

代码如下:

#include<iostream> using namespace std; typedef long long ll; ll gcd(ll a,ll b){ if(!b) return a; return gcd(b,a%b); } ll get(ll x){ ll res=x; for(ll i=2;i*i<=x;i++){ if(x%i==0){ res=res/i*(i-1); while(x%i==0) x/=i; } } if(x>1) res=res/x*(x-1); return res; } ll mul(ll a,ll b,ll p){ ll ans=0; while(b){ if(b&1) ans=(ans+a)%p; a=(a+a)%p; b>>=1; } return ans; } ll qmi(ll a,ll b,ll p){ ll ans=1; while(b){ if(b&1) ans=mul(ans,a,p); a=mul(a,a,p); b>>=1; } return ans; } int main(){ ll L; int cnt=0; while(cin>>L && L){ ll d=gcd(L,8); L=9*L/d; if(gcd(L,10)!=1){//欧拉定理的条件 printf("Case %d: 0\n",++cnt); continue; } ll eul=get(L); ll res=1e18; for(ll i=1;i*i<=eul;i++){ if(eul%i==0){ if(qmi(10,i,L)==1) res=min(res,i); if(qmi(10,eul/i,L)==1) res=min(res,eul/i); } } if(res==1e18) res=0; printf("Case %d: %lld\n",++cnt,res); } return 0; }

类型2:一般形式的指数型方程a^{x}\equiv b\left ( mod \: p \right )(其中a,p互质)

这类题目要采用特殊的算法:Baby Step,Giant Step algorithm(小步大步算法)。大致思路是:设x=i * t – j。(其中t=\sqrt{p},且0\leq i,j\leq t)那么容易知道有a^{i*t-j}\equiv b\left ( mod\: p \right ),即a^{i*t}\equiv b*a^{j}\left ( mod\, p \right )。那么我们考虑预处理出所有的a^{i*t}%p的值,存储在一个Hash表中。然后同样的把等号右边的式子的所有可能结果算出来,然后在Hash表中寻找对应相等的值,如果答案符合题意就输出。

但是为什么要取一个这样的t呢?这需要证明:

关于同余的问题

代码如下:

ll baby_step(ll a,ll b,ll p){ map<ll,ll> has;//Hash表 ll t=(ll)sqrt(p)+1; for(ll j=0;j<t;j++){ ll val=b%p*qmi(a,j,p)%p; has[val]=j; } a=qmi(a,t,p); if(!a) return b==0 ? 1 : -1;//a=b=0的情况 for(ll i=0;i<=t;i++){ ll val=qmi(a,i,p); ll j=has.find(val)==has.end() ? -1 : has[val]; if(j>=0 && i*t-j>=0){ return i*t-j; } } return -1; }

例题: TJOI2007、

2.Exgcd算法(求解线性同余方程)

引理:Bezout定理

对于任意整数a,b,存在整数x,y,使得ax+by=gcd(a,b)。

扩展到n元形式:对于任意整数a_{1},a_{2},...,a_{n},存在整数x_{1},x_{2},...,x_{n},使得\sum_{i=1}^{n}{a_{i}x_{i}}=gcd\left ( a_{1},a_{2},...,a_{n} \right )。此处证明可以考虑用合并递推的思想:首先令A=gcd(a,b),B=c,那么显然存在解x,y使得Ax+By=gcd(A,B)成立,那么带入式子计算可知三元的形式仍然成立,以此类推,不断合并即可证明。

这个就很简单了,可以自己复习啦。但是复习的时候印象不深是怎么回事?原来是采用了自己不好理解的写法,这里有一种非常直观容易理解的方法,忘了也可以推的办法:

#include<iostream> using namespace std; int exgcd(int a,int b,int &x,int &y){ if(!b){ x=1;y=0; return a; } int d=exgcd(b,a%b,x,y); int temp=x;//要注意这里所有的变量所存的值的含义 x=y; y=temp-a/b*x; return d; } int main(){ int a,b,x,y; cin>>a>>b; exgcd(a,b,x,y); cout<<(x%b+b)%b; return 0; }

推荐模板题:

AcWing 203.同余方程 

提示:这类问题可以用于解一元二次的不定方程(或是一元一次的同余方程)。

例题1:AcWing 222.青蛙的约会

这题实际上很简单,首先可以列出两个同余方程(均在模L意义下):

\left\{\begin{matrix} x + mt=x_{0} \\ y+nt=x_{0} \end{matrix}\right.

那么二式相减就可以得到一个一元一次同余方程,可以使用exgcd解决。

3.中国剩余定理及其扩展(CRT and ExCRT)

首先介绍CRT:见《算法进阶指南》P154。CRT的条件很严苛,要求模数必须两两互质。

注意这里求出来的解是一个特解,其通解的形式要特别注意:相当于把解对于所有模数的乘积取模了。

模板题:AcWing 223.阿九大战朱最学

#include<iostream> using namespace std; const int N=11; typedef long long ll; ll a[N],b[N]; ll exgcd(ll a,ll b,ll &x,ll &y){ if(!b){ x=1,y=0; return a; } ll d=exgcd(b,a%b,x,y); ll temp=x; x=y; y=temp-a/b*x; return d; } int main(){ int n; cin>>n; ll m=1; for(int i=1;i<=n;i++){ cin>>a[i]>>b[i]; m*=a[i]; } ll ans=0; for(int i=1;i<=n;i++){ ll M=m/a[i]; ll x,y; ll d=exgcd(M,a[i],x,y); ans=(ans+b[i]*M%m*x%m)%m; } cout<<(ans+m)%m; return 0; }

但是ExCRT就没有这个约束条件了,随便什么都行。那么这时考虑使用数学归纳法,假设已经求出了前k-1个方程的一个特解x,那么通解可以表示为x+tm(t\in \mathbb{Z}),其中m是前面k-1个模数的lcm。那么求前k个方程的特解相当于求一个整数t,使得x+tm\equivak(mod mk),这是一个线性同余方程,可以用扩欧解决。最后前k个方程的特解就可以表示为x’=x+tm了(i\in \mathbb{Z})。

代码如下:

#include<iostream> using namespace std; typedef long long ll; ll exgcd(ll a,ll b,ll &x,ll &y){ if(!b){ x=1,y=0; return a; } ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } ll mod(ll a,ll b){ return (a%b+b)%b; } int main(){ int n; cin>>n; ll a1,a2,m1,m2,k1,k2,ans; cin>>a1>>m1;//这里采用迭代的方法来求解 for(int i=2;i<=n;i++){ cin>>a2>>m2; ll d=exgcd(a1,-a2,k1,k2); if((m2-m1)%d!=0){//判断方程是否有解 cout<<-1<<endl; return 0; } k1=mod(k1*(m2-m1)/d,abs(a2/d)); m1=a1*k1+m1; a1=abs(a1/d*a2); } cout<<m1<<endl; return 0; }

总结:同余是数论中非常重要的板块,常会用到同余与整除的一些重要性质,所以我们要多翻看数论书,多复习这些结论,并结合一些练习,使得我们可以做到灵活运用。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/146790.html

(0)
上一篇 2025-04-12 13:26
下一篇 2025-04-12 13:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信