【学习笔记】数学知识-同余

同余

  • 取模运算性质
    • (a+b)modp=((amodp)+(bmodp))modp
    • (ab)modp=((amodp)(bmodp))modp
    • (a×b)modp=((amodp)×(bmodp))modp
    • 引流两篇讲解负数取模的文章 link1 | link2
  • 若整数 a 和整数 b 除以正整数 m 的余数相等,则称 a,bm 同余,记为 ab(modm) ;否则,称 a,bm 不同余,记为 ab(modm)
    • 性质
      • 自反性: aa(modp)
      • 对称性:若 ab(modp) ,则 ba(modp)
      • 传递性:若 ab(modp),bc(modp) ,则 ac(modp) ;若 ab(modp),q|p ,则 ab(modq)
      • 同加性:若 ab(modp) ,则 a+cb+c(modp),acbc(modp)
      • 同乘性:若 ab(modp) ,则 a×cb×c(modp)
      • acbc(modp) ,则 ab(modpgcd(c,p))
      • 同幂性:若 ab(modp) ,则 anbn(modp)
      • ab(modp),ab(modq) ,则 ab(modlcm(p,q))
      • ab(modp) ,则 p|(ab)
  • 同余类
    • 对于 a[0,p1] ,集合 {a+kp}(kZ) 的所有数模 p 同余,余数都为 a 。该集合称为一个模 p 的同余类,简记为 a
  • 剩余系
    • p 的同余类一共有 p 个,分别为 0,1,2,,p1 。它们构成 p 的完全剩余系。
    • 1p 中与 p 互质的数代表的同余类共有 φ(p) 个,它们构成 p 的简化剩余系。
      • p 的简化剩余系中的数满足与 p 互质且模 p 互不相同。
    • 性质
      • 若整数 a,b(1a,bp),p 满足 gcd(a,p)=gcd(b,p)=1 ,有 a,b,(a×b)modp 属于 p 的简化剩余系。
      • 若整数 a,b,x,p 满足 gcd(a,p)=1 ,则当 x 跑遍模 p 的简化剩余系时, ax 也跑遍模 p 的简化剩余系;当 x 跑遍模 p 的完全剩余系时, ax 也跑遍模 p 的完全剩余系;当 x 跑遍模 p 的完全剩余系时, ax+b 也跑遍模 p 的完全剩余系。

费马小定理

  • 费马小定理:若 p 是质数,则对于任意整数 a ,有 apa(modp)
    • 证明
      • amodp=0 时,显然结论成立。
      • amodp0 时,不存在一组 x,y 满足 1x,y<p,xaya(modp) 。因此 1p1 所有数,乘以 a 之后对 p 取模,仍可得 1p1 所有数。即 i=1p1ii=1p1ai(modp) ,易知其中 i=1p1ip 互质,则 i=1p1a1(modp) ,即 ap11(modp) 。等式两边同乘 a ,得到 apa(modp)
  • 变形
    • p 是质数,则对于任意整数 a ,有 ap1{0amodp=01amodp0(modp)

欧拉定理

  • 欧拉定理:若正整数 a,p 满足 gcd(a,p)=1 ,则 aφ(p)1(modp)
  • 证明
    • p 为质数时,有 aφ(p)=ap1 ,依据费马小定理,有 apa(modp) ,故 ap11(modp)
    • p 不为质数时,设 S={p1,p2,,pφ(p)}p 的简化剩余系,对于任意一对 i,j(1i,jφ(p),ij) ,有 (a×pi)modp(a×pj)modp ,且 a×pi,a×pj 均与 p 互质。因此 S 中所有数,乘以 a 之后对 p 取模,仍可得 S 中所有数,则 i=1φ(p)pii=1φ(p)api(modp) ,易知其中 i=1φ(p)pip 互质,即 i=1φ(p)a1(modp) ,即 aφ(p)1(modp)
  • 应用
    • 若正整数 a,p 满足 gcd(a,p)=1 ,则满足 ax1(modp) 的最小正整数 x0 满足 x0|φ(p)
      • 证明
        • 考虑反证法。假设满足 ax1(modp) 的最小正整数 x0 不满足 x0|φ(p) 。设 φ(p)=qx0+r(0<r<x0) 。因为 ax01(modp) ,所以 aqx01(modp) 。根据欧拉定理,有 aφ(p)1(modp) ,所以 ar1(modp) 。这与 x0 最小矛盾。因此假设不成立,故 x0|φ(p)
    • 若正整数 a,p,u,v 满足 gcd(a,p)=1,au1(modp),av1(modp) ,则 agcd(u,v)1(modp)
      • 证明
        • ux+vy=gcd(u,v) ,取它的一组解满足 x>0,y0 ,则 agcd(u,v)agcd(u,v)(av)y=agcd(u,v)vy=aux=(au)x1x1(modp)

扩展欧拉定理

  • 扩展欧拉定理:对于正整数 a,b,p 均有 ab{abmodφ(p)gcd(a,p)=1abgcd(a,p)1,b<φ(p)abmodφ(p)+φ(p)gcd(a,p)1,bφ(p)(modp)
  • 证明
    • gcd(a,p)=1 时,设 b=q×φ(p)+r ,其中 0r<φ(p) ,即 r=bmodφ(p) 。则有 ab=aq×φ(p)+r=(aφ(p))q×ar1q×arar=abmodφ(p)(modp)
    • gcd(a,p)1 时的证明极其复杂,请参考 link 或者 OI WiKi 中的证明。
  • 例题

乘法逆元

  • 乘法逆元:若关于整数 a,b 的线性同余方程 ax1(modb) 存在解,则将 x 称作 ab 的乘法逆元(简称逆元),记作 a1(modb) ,在不会引起误解时常常简记为 a1
    • 貌似数奥中把这个叫做数论倒数。
    • b|a 时不存在 a 的逆元 a1(modb)
    • 特别地,有 111(modb)
      • 证明:对于 bZ ,有 1×11(modb) ,故 1b 的逆元为 1
  • 性质
    • n 为正整数,则 21n+1(mod2n+1)
      • 证明:已知 2x1(mod2n+1) ,设 2x=(2n+1)k+1 ,又因为 2x 为偶数, k 的最小正整数解为 1 ,代入得 x=n+1
    • b 为质数,且 a<b ,则 a1ab2(modb)
      • 证明:依据费马小定理,有 ab11(modb) ,即 a×ab21(modb) ,则 ab 的乘法逆元为 ab2
    • gcd(a,b)=1 ,有 (a1)1a(modb)
  • 如何求逆元(边算边取模)
    • 扩展欧几里得
      • 限制条件: gcd(a,b)=1

      • ax=bk+1,y=k ,原方程可改写为 ax+by=1 ,用 exgcd 解得一组特解 x0,y0x 的最小正整数解为 (x+b)modb

      • 时间复杂度为 O(logmax(a,b))

      • 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;
        }
        
      • luogu P2613 【模板】有理数取余

        点击查看代码
        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;
        }
        
    • 快速幂+费马小定理
      • 限制条件: b 为质数。

      • 因为 b 为质数, ax1(modb) ,依据费马小定理,则 ax1ab1(modb) ,故 xab2(modb) 。用快速幂求出 ab2modb ,即为所求。

      • 时间复杂度为 O(logb)

      • 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;
        }
        
    • 快速幂+欧拉定理
      • 限制条件: gcd(a,b)=1

      • 因为 gcd(a,b)=1,ax1(modb) ,依据欧拉定理,则 ax1aφ(b)(modb) ,故 xaφ(b)1(modb) 。用快速幂求出 aφ(b)1modb ,即为所求。

      • 如果不预处理欧拉函数,时间复杂度为 O(b×logφ(b))

      • 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;
        }
        
    • 线性求 1n 或单个数的逆元(递推/递归)
      • 限制条件: b 为质数。

      • k=bi,r=bmodi ,此时有 b=k×i+r0(modb) 。两边同时乘以 i1×r1k×r1+i10(modb) ,移项得 i1k×r1(modb) ,将 k=bi,r=bmodi 代入得 i1bi×(bmodi)1(modb) ,考虑消除负数取模对答案的影响,故推出逆元:i1{1,i=1(bbi)×(bmodi)1i1(modb)

      • 递推求 n 个数的逆元, O(n) 预处理, O(1) 查询。

      • 递归+记忆化求 n 个数的逆元, O(n) 预处理, O(1) 查询。

        • 递归求任意一个正整数 n 的逆元,时间复杂度为 O(n13)
      • luogu P3811 【模板】模意义下的乘法逆元

        点击查看代码
        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;
        }
        
    • 线性求任意 n 个数的逆元(离线)
      • 限制条件: b 为质数。
      • 对于 1in ,令 mul[i]=k=1iak 。利用 exgcd 或快速幂计算 invc[n]=mul[n]1 。对于 1in1 ,此时有 invc[i]=invc[i+1]×ai+1=mul[i]1 。对于 1in ,有 ai1=mul[i1]×invc[i]
      • 时间复杂度为 O(n+logb)
      • 例题
        • luogu P3811 【模板】模意义下的乘法逆元

          点击查看代码
          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 P5431 【模板】模意义下的乘法逆元 2

        • LibreOJ 161. 乘法逆元 2

  • 例题

威尔逊定理

  • 威尔逊定理:若 p 为质数,则有 (p1)!1(modp)
  • 证明
    • p=2 时,因为规定 0!=1 ,所以原结论成立。
    • p2 时,根据逆元定义,对于 i,j[2,p2] ,且 ij ,均有 i1(modp),j1(modp)[2,p2],i1(modp)j1(modp) ,所以逆元是一一对应的,故 (p1)!1p32×1×(p1)1(modp)
  • 例题
    • luogu T340175 Fermat-1
      • n!+1 的次小约数等价于 n!+1 的最小质因子。故若 d 不为质数,则无解;若 d 为质数,依据威尔逊定理,易知 d1 即为满足题意的一组解。

中国剩余定理(孙子定理)

  • 中国剩余定理(孙子定理):给定 n 个两两互质的整数 m1,m2,,mn ,设 mul=i=1nmi,ri=mulmi ,则对于任意的 n 个整数 a1,a2,,an ,方程组 {xa1(modm1)xa2(modm2)xan(modmn) 在模 mul 意义下的唯一解为 x=i=1nairi(ri1(modmul)) ,通解可以表示为 x+k×mul(kZ)

  • 证明

    • 原方程组在模 mul 意义下的存在解。
      • 因为 ri=mulmi 是除 mi 所有模数的倍数,所以对于任意整数 j(1jn) 均有 ajrj(rj1(modmul)){aii=j0ij(modmi) ,所以代入 x=i=1nairi(ri1(modmul)) ,原方程组成立。
    • 原方程组在模 mul 意义下的存在唯一解。
      • 假设原方程组在模 mul 意义下的存在第二个解 x ,则一定存在一个 i 满足 xx(modmi) ,故假设不成立,原结论成立。
  • 代码实现

    点击查看代码
    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;
    }
    
  • 例题

扩展中国剩余定理

  • 扩展中国剩余定理所解决的问题和中国剩余定理所解决的问题相同,但不保证 mi 两两互质。

  • 算法流程

    • 对于第 1 个方程,有 x=a1 是第 1 个方程的一个解。
    • 设已经求出了前 i1 个方程所构成的方程组的一个解 x ,且有 mul=lcm(m1,m2,,mi1),l=xmodmul ,则 x+k×mul(kZ) 是前 i1 个方程所构成的方程组的通解,即 xl(modmul)
    • 对于第 i 个方程,其与前 i1 个方程组成了一个方程组 {xl(modmul)xai(modmi) ,即 k×mul+y×mi=ail ,此方程有解当且仅当 gcd(mul,mi)|(ail) 。若有解,则 x=x+k×mul 是前 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] 屠龙勇士
      • si 表示攻击第 i 头巨龙时所用剑的攻击力,则转换为求方程组 {s1xa1(modm1)s2xa2(modm2)snxan(modmn) 不小于 maxi=1n{aisi} 的最小非负整数解。接下来和扩展剩余定理推导内容除 mul 的取值外基本一致。

      • 设已经求出了前 i1 个方程所构成的方程组的一个解 x ,且有 mul=lcm(m1gcd(m1,s1),m2gcd(m2,s2),,mi1gcd(mi1,si1)),l=xmodmul ,则 x+k×mul(kZ) 是前 i1 个方程所构成的方程组的通解,即 xl(modmul)

        • i 个方程实际上是 xx(modmigcd(mi,si)) ,其中 xsix+miy=ai 的一组特解。故在合并同余方程时,模数已经发生由原来的 mi 变成了 migcd(mi,si)
      • 对于第 i 个方程,其与前 i1 个方程组成了一个方程组 {xl(modmul)sixai(modmi) ,即 k×mul×si+y×mi=ail×si ,此方程有解当且仅当 gcd(mul×si,mi)|(ail×si) 。若有解,则 x=x+k×mul 是前 i 个方程所构成的方程组的一个解。

        点击查看代码
        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;
        }
        

高次同余方程

  • 形如 axb(modp) ,其中已知 a,b,p ,求非负整数解 x
    • gcd(a,p)=1 ,但不保证 pP 时,使用 Baby Step,Giant Step /大步小步算法求解。
    • 当不保证 gcd(a,p)=1 ,但 pP 时,可以转化为 Baby Step,Giant Step 求解。
    • 当不保证 gcd(a,p)=1 和不保证 pP 时,使用扩展 Baby Step,Giant Step 算法求解。
      • d1=gcd(a,p) ,若 d1b 则无解,否则同余两边同时除以 d1 ,得到 ad1ax1bd1(modpd1)

      • a,pd1 仍不互质则继续取 d2=gcd(a,pd1) ,同余两边同时除以 d2 得到 a2d1d2ax2bd1d2(modpd1d2)

      • 重复这个过程直至 gcd(a,pi=1kdi)=1 ,此时有 aki=1kdiaxkbi=1kdi(modpi=1kdi)

      • 因为 gcd(a,pi=1kdi)=1 ,所以 gcd(aki=1kdi,pi=1kdi)=1 ,乘过去后求逆元就和正常的 Baby Step,Giant Step 算法一样了。

      • 对于 x<k 的情况,在求 k 的过程中判断即可。

      • 代码实现

        点击查看代码
        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;
        	}
        }
        
      • 例题

  • 形如 xab(modp) ,其中已知 a,b,p ,求非负整数解 x
posted @   hzoi_Shadow  阅读(119)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
扩大
缩小
点击右上角即可分享
微信分享提示