BSGS

前言

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

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

讲解

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

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

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

[A^x≡Bpmod P ]

一般是找最小的(x)

1.普通BSGS

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

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

(ecause A^0≡1pmod P)

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

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

(1).暴力

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

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

(2).普通BSGS

而如果我们采用( ext{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})时时间复杂度最优

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

点击查看代码
void BSGS(int A,int B,int P)
{
	if(B == 1) {Put(0,'
'); 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,'
');
			return;
		}
	}
	printf("no solution
");
}

2.扩展BSGS

如果((A,P) eq1)怎么办?

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

[A^x+kP=B(kin Z) ]

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

继续改写方程:

[frac{A}{d}A^{x-1}+kfrac{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,'
'); return;}
	int a = 1,cnt = 0,d;
	while(122520)
	{
		if((d = gcd(A,P)) == 1) break;
		if(B % d) {printf("No Solution
");return;}
		P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
		if(B == a) {Put(cnt,'
');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,'
');
			return;
		}
	}
	printf("No Solution
");
}

练习

普通( ext{BSGS})

Discrete Logging (洛谷)

扩展( ext{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,'
'); 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,'
');
			return;
		}
	}
	printf("no solution
");
}

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,'
'); return;}
	int a = 1,cnt = 0,d;
	while(122520)
	{
		if((d = gcd(A,P)) == 1) break;
		if(B % d) {printf("No Solution
");return;}
		P /= d; B /= d; ++cnt; a = 1ll * a * A / d % P;
		if(B == a) {Put(cnt,'
');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,'
');
			return;
		}
	}
	printf("No Solution
");
}

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;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/13392259.html