【学习笔记】数学知识-同余
同余
- 取模运算性质
- 若整数
和整数 除以正整数 的余数相等,则称 模 同余,记为 ;否则,称 模 不同余,记为 。- 性质
- 自反性:
- 对称性:若
,则 。 - 传递性:若
,则 ;若 ,则 。 - 同加性:若
,则 。 - 同乘性:若
,则 。 - 若
,则 。 - 同幂性:若
,则 。 - 若
,则 。 - 若
,则 。
- 自反性:
- 性质
- 同余类
- 对于
,集合 的所有数模 同余,余数都为 。该集合称为一个模 的同余类,简记为 。
- 对于
- 剩余系
- 模
的同余类一共有 个,分别为 。它们构成 的完全剩余系。 中与 互质的数代表的同余类共有 个,它们构成 的简化剩余系。 的简化剩余系中的数满足与 互质且模 互不相同。
- 性质
- 若整数
满足 ,有 属于 的简化剩余系。 - 若整数
满足 ,则当 跑遍模 的简化剩余系时, 也跑遍模 的简化剩余系;当 跑遍模 的完全剩余系时, 也跑遍模 的完全剩余系;当 跑遍模 的完全剩余系时, 也跑遍模 的完全剩余系。
- 若整数
- 模
费马小定理
- 费马小定理:若
是质数,则对于任意整数 ,有 。- 证明
- 当
时,显然结论成立。 - 当
时,不存在一组 满足 。因此 所有数,乘以 之后对 取模,仍可得 所有数。即 ,易知其中 与 互质,则 ,即 。等式两边同乘 ,得到 。
- 当
- 证明
- 变形
- 若
是质数,则对于任意整数 ,有 。
- 若
欧拉定理
- 欧拉定理:若正整数
满足 ,则 。 - 证明
- 当
为质数时,有 ,依据费马小定理,有 ,故 。 - 当
不为质数时,设 为 的简化剩余系,对于任意一对 ,有 ,且 均与 互质。因此 中所有数,乘以 之后对 取模,仍可得 中所有数,则 ,易知其中 与 互质,即 ,即 。
- 当
- 应用
- 若正整数
满足 ,则满足 的最小正整数 满足 。- 证明
- 考虑反证法。假设满足
的最小正整数 不满足 。设 。因为 ,所以 。根据欧拉定理,有 ,所以 。这与 最小矛盾。因此假设不成立,故 。
- 考虑反证法。假设满足
- 证明
- 若正整数
满足 ,则 。- 证明
- 设
,取它的一组解满足 ,则 。
- 设
- 证明
- 若正整数
扩展欧拉定理
- 扩展欧拉定理:对于正整数
均有 。 - 证明
- 例题
-
点击查看代码
ll read(ll p) { ll x=0,f=1,f2=0; char c=getchar(); while(c<'0'||'9'<c) { if(c=='-') { f=-1; } c=getchar(); } while('0'<=c&&c<='9') { x=x*10+c-'0'; if(x>=p)//当b>=phi(p) 时,公式才能应用 { f2=1; } x%=p; c=getchar(); } if(f2==1) { x+=p; } return x; } ll phi(ll n) { ll ans=n,i; for(i=2;i<=sqrt(n);i++) { if(n%i==0) { ans=ans/i*(i-1); while(n%i==0) { n/=i; } } } if(n>1) { ans=ans/n*(n-1); } return ans; } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans%p; } int main() { ll a,b,phii; cin>>a>>p; phii=phi(p); b=read(phii); cout<<qpow(a,b,p)<<endl; return 0; }
-
乘法逆元
- 乘法逆元:若关于整数
的线性同余方程 存在解,则将 称作 模 的乘法逆元(简称逆元),记作 ,在不会引起误解时常常简记为 。- 貌似数奥中把这个叫做数论倒数。
- 若
时不存在 的逆元 。 - 特别地,有
。- 证明:对于
,有 ,故 模 的逆元为 。
- 证明:对于
- 性质
- 若
为正整数,则 。- 证明:已知
,设 ,又因为 为偶数, 的最小正整数解为 ,代入得 。
- 证明:已知
- 若
为质数,且 ,则 。- 证明:依据费马小定理,有
,即 ,则 模 的乘法逆元为 。
- 证明:依据费马小定理,有
- 若
,有 。
- 若
- 如何求逆元(边算边取模)
- 扩展欧几里得
-
限制条件:
。 -
设
,原方程可改写为 ,用 解得一组特解 , 的最小正整数解为 。 -
时间复杂度为
。 -
luogu P1082 [NOIP2012 提高组] 同余方程
点击查看代码
ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return a; } else { ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } } int main() { ll a,b,x=0,y=0; cin>>a>>b; exgcd(a,b,x,y); x=(x%b+b)%b; cout<<x<<endl; }
-
点击查看代码
const ll p=19260817; ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return a; } else { ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } } int main() { ll a,c,d,x=0,y=0; c=read(); a=read(); d=exgcd(a,p,x,y); if(c%d==0) { x=(x*c/d)%p; x=(x%p+p)%p; cout<<x<<endl; } else { cout<<"Angry!"<<endl; } return 0; }
-
- 快速幂+费马小定理
-
限制条件:
为质数。 -
因为
为质数, ,依据费马小定理,则 ,故 。用快速幂求出 ,即为所求。 -
时间复杂度为
。 -
luogu P1082 [NOIP2012 提高组] 同余方程
点击查看代码
ll qpow(ll a,ll b,ll p) { ll ans=1; a%=p; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans%p; } int main() { ll a,b; cin>>a>>b; cout<<qpow(a,b-2,b)<<endl; }
-
- 快速幂+欧拉定理
-
限制条件:
。 -
因为
,依据欧拉定理,则 ,故 。用快速幂求出 ,即为所求。 -
如果不预处理欧拉函数,时间复杂度为
。 -
luogu P1082 [NOIP2012 提高组] 同余方程
点击查看代码
ll phi(ll n) { ll ans=n,i; for(i=2;i<=sqrt(n);i++) { if(n%i==0) { ans=ans/i*(i-1); while(n%i==0) { n/=i; } } } if(n>1) { ans=ans/n*(n-1); } return ans; } ll qpow(ll a,ll b,ll p) { ll ans=1; a%=p; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans%p; } int main() { ll a,b; cin>>a>>b; cout<<qpow(a,phi(b)-1,b)<<endl; }
-
- 线性求
或单个数的逆元(递推/递归)-
限制条件:
为质数。 -
设
,此时有 。两边同时乘以 得 ,移项得 ,将 代入得 ,考虑消除负数取模对答案的影响,故推出逆元: -
递推求
个数的逆元, 预处理, 查询。 -
递归+记忆化求
个数的逆元, 预处理, 查询。- 递归求任意一个正整数
的逆元,时间复杂度为 。
- 递归求任意一个正整数
-
点击查看代码
ll inv[3000001]; int main() { ll n,p,i; cin>>n>>p; inv[1]=1; cout<<"1"<<endl; for(i=2;i<=n;i++) { inv[i]=(p-p/i)*inv[p%i]%p; cout<<inv[i]<<endl; } return 0; }
-
- 线性求任意
个数的逆元(离线)- 限制条件:
为质数。 - 对于
,令 。利用 或快速幂计算 。对于 ,此时有 。对于 ,有 。 - 时间复杂度为
。 - 例题
-
点击查看代码
ll mul[3000001],invc[3000001],inv[3000001],w[3000001];//防止重名,此处的w[]即为上文的a[] ll qpow(ll a,ll b,ll p) { ll ans=1; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans%p; } int main() { ll n,p,i; cin>>n>>p; mul[0]=1; for(i=1;i<=n;i++) { w[i]=i; mul[i]=((mul[i-1]%p)*(w[i]%p))%p; } invc[n]=qpow(mul[n],p-2,p); for(i=n-1;i>=1;i--) { invc[i]=((invc[i+1]%p)*((w[i+1])%p))%p; } for(i=1;i<=n;i++) { inv[i]=((invc[i]%p)*(mul[i-1]%p))%p; cout<<inv[i]<<endl; } return 0; }
-
- 限制条件:
- 扩展欧几里得
- 例题
威尔逊定理
- 威尔逊定理:若
为质数,则有 。 - 证明
- 当
时,因为规定 ,所以原结论成立。 - 当
时,根据逆元定义,对于 ,且 ,均有 ,所以逆元是一一对应的,故 。
- 当
- 例题
- luogu T340175 Fermat-1
的次小约数等价于 的最小质因子。故若 不为质数,则无解;若 为质数,依据威尔逊定理,易知 即为满足题意的一组解。
- luogu T340175 Fermat-1
中国剩余定理(孙子定理)
-
中国剩余定理(孙子定理):给定
个两两互质的整数 ,设 ,则对于任意的 个整数 ,方程组 在模 意义下的唯一解为 ,通解可以表示为 。 -
证明
- 原方程组在模
意义下的存在解。- 因为
是除 所有模数的倍数,所以对于任意整数 均有 ,所以代入 ,原方程组成立。
- 因为
- 原方程组在模
意义下的存在唯一解。- 假设原方程组在模
意义下的存在第二个解 ,则一定存在一个 满足 ,故假设不成立,原结论成立。
- 假设原方程组在模
- 原方程组在模
-
代码实现
点击查看代码
ll crt(ll n) { ll mul=1,ans=0,x,y,r,i; for(i=1;i<=n;i++) { mul*=m[i]; } for(i=1;i<=n;i++) { r=mul/m[i]; x=y=0; exgcd(r,m[i],x,y);//m_i两两互质并不代表m_i为质数 ans=(ans+(a[i]*r%mul)*(x+m[i])%mul)%mul;//因为exgcd求出的x可能为负数,故需要+m[i] } return ans; }
-
例题
- luogu P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
- UVA756 Biorhythms
- 注意解的下限是有限制的。
- luogu P3868 [TJOI2009] 猜数字
- 因为
可能为负数,所以需要有a[i]=(a[i]+m[i])%m[i];
。
- 因为
扩展中国剩余定理
-
扩展中国剩余定理所解决的问题和中国剩余定理所解决的问题相同,但不保证
两两互质。 -
算法流程
- 对于第
个方程,有 是第 个方程的一个解。 - 设已经求出了前
个方程所构成的方程组的一个解 ,且有 ,则 是前 个方程所构成的方程组的通解,即 。 - 对于第
个方程,其与前 个方程组成了一个方程组 ,即 ,此方程有解当且仅当 。若有解,则 是前 个方程所构成的方程组的一个解。
- 对于第
-
代码实现
点击查看代码
ll excrt(ll n) { ll mul=m[1],ans=a[1],x,y,c,d,i; for(i=2;i<=n;i++) { x=y=0; c=(a[i]-ans%m[i]+m[i])%m[i];//减法取模注意先加模数再取模 d=exgcd(mul,m[i],x,y); if(c%d==0) { ans+=(((x+m[i])%m[i])*(c/d)%m[i])*mul;//exgcd解出来的x可能是负数 mul=lcm(mul,m[i]); ans=(ans%mul+mul)%mul; } else { return -1; } } return (ans+mul)%mul; }
-
例题
- luogu P4777 【模板】扩展中国剩余定理(EXCRT)
- LibreOJ 10213. 「一本通 6.4 例 5」Strange Way to Express Integers
- CF787A The Monster
- 注意解的下限是有限制的。
- luogu P4774 [NOI2018] 屠龙勇士
-
设
表示攻击第 头巨龙时所用剑的攻击力,则转换为求方程组 不小于 的最小非负整数解。接下来和扩展剩余定理推导内容除 的取值外基本一致。 -
设已经求出了前
个方程所构成的方程组的一个解 ,且有 ,则 是前 个方程所构成的方程组的通解,即 。- 第
个方程实际上是 ,其中 是 的一组特解。故在合并同余方程时,模数已经发生由原来的 变成了 。
- 第
-
对于第
个方程,其与前 个方程组成了一个方程组 ,即 ,此方程有解当且仅当 。若有解,则 是前 个方程所构成的方程组的一个解。点击查看代码
ll excrt(ll n) { ll mul=1,ans=0,x,y,c,d,i,maxx=0; for(i=1;i<=n;i++) { x=y=0; it=s.upper_bound(a[i]);//注意是不大于 if(it!=s.begin()) { it--; } maxx=max(maxx,(ll)ceil(1.0*a[i]/(*it))); c=(a[i]-((*it)%m[i])*(ans%m[i])%m[i]+m[i])%m[i]; d=exgcd((*it)*mul,m[i],x,y); if(c%d==0) { ans+=(((x+m[i])%m[i])*(c/d)%m[i])*mul; mul=lcm(mul,m[i]/gcd(m[i],(*it))); ans=(ans%mul+mul)%mul; } else { return -1; } s.erase(it); s.insert(atk[i]); } return ans+(ll)ceil(1.0*(maxx-ans)/mul)*mul; }
-
高次同余方程
- 形如
,其中已知 ,求非负整数解 。- 当
,但不保证 时,使用Baby Step,Giant Step
/大步小步算法求解。-
由欧拉定理,有
,即循环节为 ,故只找到最小的一个 即可得到所有满足条件的 。 -
算法流程
- 设
,其中 ,则方程改写为 。 - 又因为
互质,移项得到- 同时也解释了为什么要求
互质。
- 同时也解释了为什么要求
- 对于每个
,将 插入到一个哈希表中,接着枚举 ,计算出 后在哈希表中查找即可。
- 设
-
代码实现
点击查看代码
ll qpow(ll a,ll b,ll p) { ll ans=1; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } ll bsgs(ll a,ll b,ll p) { if(1%p==b%p) { return 0; } else { map<ll,ll>vis; ll k=sqrt(p)+1,i,sum; for(i=0;i<=k-1;i++) { vis[b*qpow(a,i,p)%p]=i; } a=qpow(a,k,p); for(i=0;i<=k;i++) { sum=qpow(a,i,p); if(vis.find(sum)!=vis.end()) { if(i*k-vis[sum]>=0) { return i*k-vis[sum]; } } } return -1; } }
-
例题
-
- 当不保证
,但 时,可以转化为Baby Step,Giant Step
求解。- 由于不保证
,故当 或 时,同余方程存在非负整数解,其中前者的最小非负整数解为 ,后者则可以通过Baby Step,Giant Step
求解;否则不存在解。 - 例题
- 由于不保证
- 当不保证
和不保证 时,使用扩展Baby Step,Giant Step
算法求解。-
设
,若 则无解,否则同余两边同时除以 ,得到 。 -
若
仍不互质则继续取 ,同余两边同时除以 得到 。 -
重复这个过程直至
,此时有 。 -
因为
,所以 ,乘过去后求逆元就和正常的Baby Step,Giant Step
算法一样了。 -
对于
的情况,在求 的过程中判断即可。 -
代码实现
点击查看代码
ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return a; } else { ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } } ll inv(ll a,ll p) { ll x,y; exgcd(a,p,x,y); return (x%p+p)%p; } ll bsgs(ll a,ll b,ll p) { if(1%p==b%p) { return 0; } else { unordered_map<ll,ll>vis; ll k=sqrt(p)+1,i,sum=1; for(i=0;i<=k-1;i++) { b=(i==0)?b:b*a%p; vis[b]=i; } a=qpow(a,k,p); for(i=0;i<=k;i++) { sum=(i==0)?sum:sum*a%p; if(vis.find(sum)!=vis.end()) { if(i*k-vis[sum]>=0) { return i*k-vis[sum]; } } } return -1; } } ll exbsgs(ll a,ll b,ll p) { b%=p;//防止后面判 b%d==0 时出错 if(b==1||p==1)//特判 0 的情况 { return 0; } else { ll x,y,d=exgcd(a,p,x,y),k=0,mul=1; while(d!=1) { if(b%d==0) { k++; b/=d; p/=d; mul=(a/d)*mul%p; if(mul==b)//已经是答案 { return k; } else { d=exgcd(a,p,x,y); } } else { return -1; } } ll ans=bsgs(a,b*inv(mul,p)%p,p); return (ans!=-1)*k+ans; } }
-
例题
-
- 当
- 形如
,其中已知 ,求非负整数解 。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/17641004.html,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】