📘数论知识,高次同余方程求解,形如: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); } }
|