📘数论知识,高次同余方程求解,形如:ax=n(modp) 。BSGS (Baby Steps Giant Steps)又名大步小步算法,又名北上广深 。
前置知识
BSGS
对于如下的同余式,且满足 a⊥p ,求 x
ax≡n(modp)
解法
首先一个显然的结论 x∈[0,p−1] ,根据欧拉定理 aφ(p)≡1(modp) ,且 φ(p)<p,x 大于 p−1 后 ax 的取值肯定在之前就出现过,要得到最小的解只要考虑 x∈[0,p−1] 即可。
令 x=A×⌈p⌉−B∣A,B∈N ,对于给定 x 且 B∈[1,⌈p⌉],一定能找到对应的 A∈[1,⌈p⌉] ,证明如下:
上式可转换为:
x+B=A×⌈p⌉
因为 (x+B) 有 ⌈p⌉ 个连续的两两不同的取值 ,所以一定存在 (x+B)mod⌈p⌉=0 ,而且 (x+B) 一定不超过 ⌈p⌉2 ,故存在 A∈[1,⌈p⌉] 使上式成立。
aA×⌈p⌉−BaA×⌈p⌉≡≡nn×aB(modp)(modp)
a,n 已知,然后可以O(p) 枚举 B ,用哈希记录下每个 n×aB 对应的 B 值,再 O(p) 枚举 A ,再哈希表中是否有与 aA×⌈p⌉ 相等的 n×aB ,若有则可以得到 x=A×⌈p⌉−B 。
总复杂是 O(p) ,使用 map 会多一个 log
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
   | #include <bits/stdc++.h> using namespace std; typedef long long ll; int gcd(ll a,ll b) {return !b?a:gcd(b,a%b);}
  ll BSGS(ll a,ll n,ll p) {     ll ans=p,t=ceil(sqrt(p)),dt=1;     map<ll,ll> hash;     for(ll B=1;B<=t;B++) dt=(dt*a)%p,hash[(dt*n)%p]=B;     for(ll A=1,num=dt;A<=t;A++,num=(num*dt)%p) if(hash.find(num)!=hash.end()) ans=min(ans,A*t-hash[num]);      return ans; } ll a,p,n; int main() {     cin>>p>>a>>n;     ll ans=BSGS(a,n,p);     if(ans==p) printf("no solution\n");     else printf("%lld\n",ans); }
   | 
 
EXBSGS
还是如下同余式,但是不保证 a⊥p
ax≡n(modp)
解法
不保证 a⊥p 就有一个问题,a 没有对 p 的逆元,故无法使用普通的BSGS解决,于是乎我们想办法使a,p 互质,令 d1=gcd(a,p) ,若 d1∤n 则无解,否则得:
d1a⋅ax−1≡d1n(modd1p)
此时,若 a⊥p,则继续令 d2=gcd(a,d1p) ,若 d2∤d1n 则无解,否则得:
d1d2a2⋅ax−2≡d1d2n(modd1d2p)
以此类推,设 D=i=1∏kdi ,则:
Dak⋅ax−k≡Dn(modDp)
这里的 k 是 log 级别的,因为每次 p 至少小了一半。此时我们已经得到了 a⊥Dp ,易证 Dak⊥Dp ,可以算出 Dak 的逆元,移到右边去:
ax−k≡Dn⋅(Dak)−1(modDp)
剩下的就是一个平凡的BSGS,算出 x−k ,答案再加上 k 即可。
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
   | #include <bits/stdc++.h> using namespace std; typedef long long ll; ll gcd(ll a,ll b) {return !b?a:gcd(b,a%b);} 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);     swap(x,y);y-=a/b*x;     return d; } ll inv(ll a,ll b) {     ll x,y;exgcd(a,b,x,y);     return (x%b+b)%b; } ll BSGS(ll a,ll n,ll p) {     ll ans=p,t=ceil(sqrt(p)),dt=1;     map<ll,ll> hash;     for(ll B=1;B<=t;B++) dt=(dt*a)%p,hash[(dt*n)%p]=B;     for(ll A=1,num=dt;A<=t;A++,num=(num*dt)%p) if(hash.find(num)!=hash.end()) ans=min(ans,A*t-hash[num]);      return ans; }
  ll EXBSGS(ll a,ll n,ll p) {     int k=0;ll d,ad=1;     while((d=gcd(a,p))!=1) {         if(n%d) return -1;         n/=d,p/=d;ad*=a/d;ad%=p;k++;     }     n=(n*inv(ad,p))%p;     ll res=BSGS(a,n,p);     if(res==p) return -1;     return res+k;  } ll a,p,n; int main() {     while(cin>>a>>p>>n) {         if(a==0&&p==0&&n==0) break;         ll ans=EXBSGS(a,n,p);         if(ans==-1) printf("No Solution\n");         else printf("%lld\n",ans);     } }
   |