小步大步算法(BSGS)求解离散对数问题
众所周知,离散对数问题(Discrete Logarithm Problem,DLP)一直被认为是困难问题。离散对数问题可以是基于循环群的,也可以是基于椭圆曲线的,本篇以循环群上的离散对数问题为例。主要描述的是这样一个问题:
DLP问题
求解同余方程
A
x
≡
B
(
m
o
d
P
)
A^{x}\equiv B \pmod{P}
Ax≡B(modP)
其中,
P
P
P是大素数。
如果是在整数上,求对数就可以解决。但是在循环群上,离散对数求解却是难之又难。小步大步(Baby Step Giant Step,BSGS)算法是求离散对数问题的一种方法(有的也称为Shanks算法)
问题分析和算法主要思想
根据费马小定理,我们知道
A
P
−
1
≡
1
(
m
o
d
P
)
A^{P-1}\equiv 1\pmod{P}
AP−1≡1(modP)
所以,如果穷举搜索
x
x
x的值,算法的复杂度是
O
(
P
)
O(P)
O(P)。BSGS算法能够将任意离散对数问题的求解复杂度降至
O
(
P
)
O(\sqrt{P})
O(P)。
假设我们事先知道一个数
m
m
m,然后将
x
x
x表示成
x
=
m
i
−
j
并
且
有
j
∈
[
0
,
m
−
1
]
x=mi-j\\ 并且有j\in\left[ 0,m-1 \right]
x=mi−j并且有j∈[0,m−1]
这样,原来的同余方程可以变化为:
A
x
≡
B
(
m
o
d
P
)
A
m
i
−
j
≡
B
(
m
o
d
P
)
A
m
i
≡
A
j
B
(
m
o
d
P
)
A^{x}\equiv B \pmod{P}\\ A^{mi-j} \equiv B \pmod{P}\\ A^{mi}\equiv A^{j}B\pmod{P}
Ax≡B(modP)Ami−j≡B(modP)Ami≡AjB(modP)
根据这个式子,可以做文章了。
- 由于 j ∈ [ 0 , m − 1 ] j\in\left[ 0,m-1 \right] j∈[0,m−1],穷举 A j A^{j} Aj的所有可能取值,存在哈希表中。复杂度为 O ( m ) O(m) O(m);
- i i i的界是 [ 0 , P m ] [0,\frac{P}{m}] [0,mP],穷举每个可能的 i i i,计算 A m i A^{mi} Ami的值,我们就可以得到 A j A^{j} Aj的值,再查表,看那个匹配。复杂度是 O ( P m ) O(\frac{P}{m}) O(mP);
- 查到满足条件的 i i i和 j j j,计算 x = m i − j x=mi-j x=mi−j即可得到解。
这个算法的时间复杂度是 O ( m + P m ) O(m+\frac{P}{m}) O(m+mP),通常我们选取 m = ⌈ P ⌉ m=\lceil \sqrt{P} \rceil m=⌈P⌉,则算法的时间复杂度为 O ( P ) O(\sqrt{P}) O(P)。
算法代码
这个代码来自[BSGS]大步小步算法,仅作参考。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
int t,k;
ll y,z,p;
//模幂算法
ll pow(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
//小步大步算法
ll BSGS(ll a,ll b,ll p){
if(!a)return b?-1:1;
if(b==1)return 0;
map<ll,ll>mp;
ll m=ceil(sqrt(p)),ax=1;
for(int i=0;i<m;i++){
mp[ax]=i;
ax=ax*a%p;
}
ll am=pow(a,m,p),aj=am*pow(b,p-2,p)%p;
for(int i=1;i<=m;i++){
if(mp.count(aj))return m*i-mp[aj];
aj=aj*am%p;
}
return -1;
}
int main(){
scanf("%d%d",&t,&k);
while(t--){
scanf("%lld%lld%lld",&y,&z,&p);
if(k==1)printf("%lld\n",pow(y,z,p));
else if(k==2){
if(y%p==0)printf("Orz, I cannot find x!\n");
else printf("%lld\n",pow(y,p-2,p)*z%p);
}else{
ll ans=BSGS(y%p,z%p,p);
if(~ans)printf("%lld\n",ans);
else printf("Orz, I cannot find x!\n");
}
}
}