小步大步算法(BSGS)求解离散对数问题

19 篇文章 21 订阅
10 篇文章 4 订阅

小步大步算法(BSGS)求解离散对数问题

众所周知,离散对数问题(Discrete Logarithm Problem,DLP)一直被认为是困难问题。离散对数问题可以是基于循环群的,也可以是基于椭圆曲线的,本篇以循环群上的离散对数问题为例。主要描述的是这样一个问题:

DLP问题

求解同余方程
A x ≡ B ( m o d P ) A^{x}\equiv B \pmod{P} AxB(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} AP11(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=mijj[0,m1]
这样,原来的同余方程可以变化为:
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} AxB(modP)AmijB(modP)AmiAjB(modP)
根据这个式子,可以做文章了。

  1. 由于 j ∈ [ 0 , m − 1 ] j\in\left[ 0,m-1 \right] j[0,m1],穷举 A j A^{j} Aj的所有可能取值,存在哈希表中。复杂度为 O ( m ) O(m) O(m)
  2. 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)
  3. 查到满足条件的 i i i j j j,计算 x = m i − j x=mi-j x=mij即可得到解。
    这个算法的时间复杂度是 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");
		}
	}
}
  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值