BSGS

前言

\(\text{BabyStepGiantStep}\)算法,即大步小步算法

又名 拔山盖世北上广深 算法

讲解

按照惯例,我们先来一波百度百科自学时间

百度百科竟然没有BSGS,我佛了

\(\text{BSGS}\)是用来求解关于\(x\)的方程,\(P\)是质数

\[A^x≡B\pmod P \]

一般是找最小的\(x\)

1.普通BSGS

\((A,P)=1\)的情况

由费马小定理,得\(A^{P-1}≡1\pmod P\)

\(\because A^0≡1\pmod P\)

\(\therefore A\)的幂存在循环节,且循环节长度为\(P-1\)

但其实\(P\)不为质数也很明显存在循环节

(1).暴力

我们直接暴力检验循环节的每一个元素就好了

时间复杂度为\(O(P)\)

(2).普通BSGS

而如果我们采用\(\text{BSGS}\)算法:

先把\(x\)写成\(im-j\)的形式(\(m\)自取)

原式可化为:

\[A^{im}≡BA^{j}\pmod P \]

对于右边每一个\(BA^j\),我们将其存进\(map\)或者哈希表中,然后枚举\(i\),在其中查找\(A^{im}\)是否出现过

找到即有解

明显时间复杂度为\(O(max(P/m,m))\)

\(m=\sqrt{P}\)时时间复杂度最优

所以普通的\(\text{BSGS}\)的时间复杂度为\(O(\sqrt{P})\)

点击查看代码
void BSGS(int A,int B,int P)
{
	if(B == 1) {Put(0,'\n'); return;}
	int m = ceil(sqrt(P)),now = B % P;
	for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
	int Am = qpow(A,m,P); now = 1;
	for(int i = 1;i <= m;++ i)
	{
		now = 1ll * Am * now % P;
		int dz = query(now);
		if(dz != -1)
		{
			Put(1ll*i*m - dz,'\n');
			return;
		}
	}
	printf("no solution\n");
}

2.扩展BSGS

如果\((A,P)\neq1\)怎么办?

\(d=(A,P)\),再将原方程化为:

\[A^x+kP=B(k\in Z) \]

明显\(d|B\),否则无解

继续改写方程:

\[\frac{A}{d}A^{x-1}+k\frac{P}{d}=\frac{B}{d} \]

这样的话\(\frac{A}{d}\)就成为了一个系数

不断检查\((A,\frac{P}{d})\),直到互质

此时方程可化为:

\[\frac{y^{cnt}}{d}y^{x-cnt}≡\frac{B}{d}\pmod {\frac{P}{d}} \]

注意此时的\(d\)是并不是最初的\(d\),而是做了\(cnt\)次操作累计起来的\(d\)

其中我的代码将\(\frac{y^{cnt}}{d}\)定为\(a\)

点击查看代码
void EXBSGS(int A,int B,int P)
{
	if(B == 1) {Put(0,'\n'); return;}
	int a = 1,cnt = 0,d;
	while(122520)
	{
		if((d = gcd(A,P)) == 1) break;
		if(B % d) {printf("No Solution\n");return;}
		P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
		if(B == a) {Put(cnt,'\n');return;}
	}
	int m = ceil(sqrt(P)),now = B;
	for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
	int Am = qpow(A,m,P); now = a;
	for(int i = 1;i <= m;++ i)
	{
		now = 1ll * Am * now % P;
		int dz = query(now);
		if(dz != -1)
		{
			Put(1ll*i*m - dz + cnt,'\n');
			return;
		}
	}
	printf("No Solution\n");
}

练习

普通\(\text{BSGS}\)

Discrete Logging (洛谷)

扩展\(\text{BSGS}\)

Clever Y (POJ)

代码

Discrete Logging

点击查看代码
//12252024832524
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; 

typedef long long LL;
const int MAXN = 10005;
const int MOD = 122520;
int head[MOD],tot;
struct node
{
	int val,ID,nxt;
}hs[46341 + 5];

int qpow(int x,int y,int P)
{
	int ret = 1;
	while(y) {if(y & 1) ret = 1ll * ret * x % P;x = 1ll * x * x % P;y >>= 1;}
	return ret;
}
void ins(int x,int ID)
{
	hs[++tot].val = x;
	x %= MOD;
	hs[tot].ID = ID;
	hs[tot].nxt = head[x];
	head[x] = tot;
}
int query(int x)
{
	for(int i = head[x % MOD]; i ;i = hs[i].nxt)
		if(hs[i].val == x) return hs[i].ID;
	return -1;
}
void BSGS(int A,int B,int P)
{
	if(B == 1) {Put(0,'\n'); return;}
	int m = ceil(sqrt(P)),now = B % P;
	for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
	int Am = qpow(A,m,P); now = 1;
	for(int i = 1;i <= m;++ i)
	{
		now = 1ll * Am * now % P;
		int dz = query(now);
		if(dz != -1)
		{
			Put(1ll*i*m - dz,'\n');
			return;
		}
	}
	printf("no solution\n");
}

int main()

	int Mod,a;
	while(~scanf("%d %d",&Mod,&a)) 
	{
		memset(head,0,sizeof(head)); tot = 0;
		BSGS(a,Read(),Mod);
	}
	return 0;
}

Clever Y

点击查看代码
void EXBSGS(int A,int B,int P)
{
	if(B == 1) {Put(0,'\n'); return;}
	int a = 1,cnt = 0,d;
	while(122520)
	{
		if((d = gcd(A,P)) == 1) break;
		if(B % d) {printf("No Solution\n");return;}
		P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
		if(B == a) {Put(cnt,'\n');return;}
	}
	int m = ceil(sqrt(P)),now = B;
	for(int i = 0;i < m;++ i) ins(now,i),now = 1ll * now * A % P;
	int Am = qpow(A,m,P); now = a;
	for(int i = 1;i <= m;++ i)
	{
		now = 1ll * Am * now % P;
		int dz = query(now);
		if(dz != -1)
		{
			Put(1ll*i*m - dz + cnt,'\n');
			return;
		}
	}
	printf("No Solution\n");
}

int main()
{
	int A,B,Mod;
	while(~scanf("%d",&A)) 
	{
		Mod = Read(); B = Read();
		if(!A && !Mod && !B) return 0;
		memset(head,0,sizeof(head)); tot = 0;
		EXBSGS(A % Mod,B % Mod,Mod);
	}
	return 0;
}
posted @ 2020-07-28 17:14  皮皮刘  阅读(473)  评论(0编辑  收藏  举报