版权声明:岂曰无衣,与子同袍 https://blog.csdn.net/sizaif/article/details/81531299
BSGS
大步小步算法,也称拔山盖世算法...
求解 : 已知 A,B,P得情况下,求解x
分为普通情况和扩栈情况.
1.先说普通情况,
P是素数(确定的), 或 P为非素数但 , ,A,P, 互质时
开始之前先证明一个结论:
如果,那么对于x∈N,有 也就是费马小定理.
证明:因为,根据欧拉定理,得
设k∈N,根据同幂性,得
设a∈N且a<φ(P),所以
即,得证。
将 x 分块 成 i*m - j m = = sqrt(p) + 1;
<;
然后 就 化简成了 :
然后可以枚举[0-m] 将 存入 哈希表中.然后 枚举[1,m] 查询 哈希表中是否有
如果有, 则 x = i*m - hash[]
注意: [0−m]枚举j,而从[1−m]枚举i 的原因是: i不能为0,否则i*m−j有可能出现负数的情况
模板题:
这道题: 已知 g,p g是p 的原根, 则 g^p = 1; 求 , 则 只需要求解 ,a
然后求 B^a 就是
可以预处里 [0-m] 然后,求解 时 呈上一个逆元 A 在进行判断[
[代码]:
#include <iostream>
#include <bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i =n;i>=a;i--)
#define Si(x) scanf("%d",&x)
//BSGS 算法
typedef long long ll;
const int maxn = 1e5+10;
const int mod = 1e9+7;
using namespace std;
map<ll,int>hashs;
ll n,p,g,A,B;
int bl;
ll qpow(ll a,ll n)
{
ll res = 1;
for(;n;n>>=1)
{
if(n&1)
res = res*a %p;
a = a*a %p;
}
return res;
}
void init()
{
hashs.clear();
bl = sqrt(p)+1;
ll ans = 1 ;
for(int i = 0 ; i <= bl;i++)
{
if(i==0)
{
hashs[g%p] = i;
continue;
}
ans = ans*g % p;
hashs[ans] = i;
}
}
void solve()
{
cin>>A>>B;
ll a;
ll base = qpow(g,bl);
ll temp = qpow(A,p-2);
for(int i = 1 ;i <= bl ;i++)
{
temp = temp*base%p;
if( hashs[temp])
{
a = i*bl - hashs[temp];
break;
}
}
B = qpow(B,a);
cout<<B<<endl;
}
int main()
{
cin>>g>>p>>n;
init();
while(n--)
{
solve();
}
return 0;
}
2.扩展BSBG
gcd(A,P)!=1 此时, 我们要想办法把 方程转化成gcd(P,A) = 1
方程改写成: ;
设 g = gcd(A,P) 如果 g 不能整出B, 则 方程无解, 当 可以整出时 两边同时整出g
得 : ; 消去一个因子后得 到 方程:
令 :
得到新的方程:
不断重复这个过程最后一定会得到一个可以解的方程,套用刚刚的BSBG解出后即可。
要注意的是在这个过程中如果某一步发现,那么就可以直接退出,因为这时候已经得到解了.
注意:整出g得时候肯会出现多次,所以要记录cnt
代码:
inline ll qpow(ll a,ll n,ll p)
{
ll res = 1;
for(;n;n>>=1)
{
if(n&1) res = res*a %p;
a = a*a%p;
}
return res;
}
int BSBG(ll a,ll b,ll p) // -1 no answer
{
a%=p,b%=p;
if(b==1) return 0;
int cnt = 0;
ll t = 1;
for(int g = __gcd(a,p); g!=1; g = __gcd(a,p))
{
if(b%g) return -1;// no answer
p/=g,b/=g,t = t*a/g%p;
++cnt;
if(b==t) return cnt;
}
map<ll,ll>Hashs;
int m = sqrt(p) +1;
ll base = b;
for(int i = 0 ; i<=m;i++)
{
Hashs[base] = i;
base = base*a%p;
}
base = qpow(a,m,p);
ll now = t;
for(int i = 1;i<=m+1;i++)
{
now = now *base %p;
if( Hashs[now])
{
return i*m-Hashs[now] +cnt;
}
}
return -1;
}