BSGS与扩展BSGS

本文介绍了在解决整数高次同余方程ax ≡ b (mod p)时,如何使用Baby Step Giant Step (BSGS)算法处理a与p互质的情况,以及当a与p不互质时如何通过扩展BSGS进行约分并求解。详细讲解了算法原理和核心代码示例,适用于编程竞赛和实际问题解决。
摘要由CSDN通过智能技术生成

线性同余方程 b x ≡ 1 ( m o d   p ) bx\equiv1(mod \,p) bx1(modp),可以用exgcd或者费马小定理解出。但对于高次同余方程 a x ≡ b ( m o d   p ) a^x\equiv b(mod \,p) axb(modp),需要用BSGS或者扩展BSGS解出。

BSGS

问题描述:对于整数a,b,p,其中a,p互质,求一个最小的非负整数x,满足 a x ≡ b ( m o d   p ) a^x\equiv b(mod \,p) axb(modp)

BSGS是Baby Step,Giant Step的缩写,又叫大小步算法。因为a,p互质,根据欧拉定理有 a φ ( p ) ≡ 1 ( m o d   p ) a^{\varphi(p)}\equiv 1 (mod \, p) aφ(p)1(modp)成立,也就是说当x>= φ ( p ) \varphi(p) φ(p)时,会出现循环节,在模p的意义下, a x a^x ax a x + φ ( p ) a^{x+\varphi(p)} ax+φ(p)意义是一样的。因此最小非负整数解如果存在,一定在 [ 0 , φ ( p ) − 1 ] [0, \varphi(p)-1] [0,φ(p)1]之间。

如果p值不大,完全可以暴力枚举求解。p值若比较大,如接近 2 32 2^{32} 232,要用到BSGS算法。

x = i ∗ t − j x=i * t - j x=itj,其中 t = ⌈ p ⌉ t = \lceil \sqrt p \rceil t=p ,0   ≤ \,\leq j j j   ≤ \,\leq t − 1 t-1 t1,0   ≤ \,\leq i i i   ≤ \,\leq t t t。t取定值,通过 i i i j j j的搭配, x x x就可以取遍 [ 0 , φ ( p ) − 1 ] [0, \varphi(p)-1] [0,φ(p)1]之间所有的数。带入:

a i ∗ t − j ≡ b ( m o d   p ) ( a t ) i ≡ b ∗ a j ( m o d   p ) \begin{aligned} a^{i*t-j}&\equiv b (mod \, p) \\ (a^t)^{i}&\equiv b*a^j (mod \, p) \end{aligned} aitj(at)ib(modp)baj(modp)
对于所有的0   ≤ \,\leq j j j   ≤ \,\leq t − 1 t-1 t1,把 b ∗ a j m o d   p b*a^j mod \, p bajmodp的结果插入一个hash表。可以用map或者拉链法实现hash表。

从小到大枚举i的可能值,即0   ≤ \,\leq i i i   ≤ \,\leq t t t,计算 ( a t ) i m o d     p (a^t)^{i} \mod\,p (at)imodp,在hash表中查找是否存在对应的 j j j,找到的第一个 i i i j j j的组合就是答案 i ∗ t − j i*t-j itj。时间复杂度 O ( p ) O(\sqrt p) O(p )。用map实现hash表时间复杂度还要带一个查找时的log。

怎么保证求出的是最小非负整数解?把 b ∗ a j m o d   p b*a^j mod \, p bajmodp的结果插入hash表时, j j j值是从小到大的,如果前后两个的 v a l val val值相同,则保存的是较大的 j j j。枚举 i i i是也是从小到大。对于表达式 i ∗ t − j i*t-j itj i i i取最小值, j j j取最大值,结果就是最小的。

核心代码:

int BSGS(int a, int b, int p)
{
//	if (a == 0 && b == 1) return 0;  // 0^0定义不存在,这种情况不需要特判  
	if (a == 0 && b == 0) return 1;
	if (a == 0 && b != 0) return -1;
	q.clear();
	int t = ceil(sqrt(p));
	b %= p;
	for (int j = 0; j < t; ++j)
	{
		int val = (long long)b * szpow(a, j, p) % p;  // 两个int相乘可能会溢出  
		q[val] = j;
	}
	a = szpow(a, t, p);
	for (int i = 0; i <= t; ++i)
	{
		int val = szpow(a, i, p);
		if (q.find(val) != q.end() && i * t - q[val] >= 0) return i * t - q[val];
	}
	return -1;
}

注意事项:在用快速幂求解 a b m o d   p a^b mod \,p abmodp时,也要开long long,否则两个int相乘可能会溢出;map实现hash表,key值可以取很大,也可以取负值,这点很方便。

int szpow(int a, int b, int p)
{
	long long r = 1, base = a;  // 不开long long会溢出 
	while (b)
	{
		if (b & 1) r = r * base % p;
		base = base * base % p;
		b >>= 1;
	}
	return r % p;
}

拉链法实现的hash表:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
using namespace std;
const int PP = 2887777;
int p, a, b;
int szfirst[PP+10], sznext[70000], cnt;
struct node
{
	int val, j;
}szdate[70000];
inline void szadd(int val, int j)
{
	int u = val % PP;
	++cnt;	
	sznext[cnt] = szfirst[u];
	szfirst[u] = cnt;
	szdate[cnt].val = val, szdate[cnt].j = j;
}
int szfind(int val)
{
	int u = val % PP;
	
	int d = szfirst[u];
	while (d)
	{
		if (szdate[d].val == val) return szdate[d].j;
		d = sznext[d];
	}
	return -1;
}
int szpow(int a, int b, int p)  
{
	long long r = 1, base = a;  // 不开long long会溢出  
	while (b)
	{
		if (b & 1) r = r * base % p;
		base = base * base % p;
		b >>= 1;
	}
	return r % p;
}
int BSGS(int a, int b, int p)
{
	if (a == 0 && b == 1) return 0;
	if (a == 0 && b == 0) return 1;
	if (a == 0 && b > 1) return -1;

	int t = ceil(sqrt(p));  // 2^16 
	b %= p;
	for (int j = 0; j < t; ++j)
	{
		int val = (long long)b * szpow(a, j, p) % p;  // 两个int相乘可能会溢出  
		szadd(val, j);
	}
	a = szpow(a, t, p);
	for (int i = 0; i <= t; ++i)
	{
		int val = szpow(a, i, p);
		int j = szfind(val);
		if (j != -1 && i * t - j >= 0) return i * t - j;
	}
	return -1;
}
int main()
{
	scanf("%d%d%d", &p, &a, &b);
	int ans;
	ans = BSGS(a, b, p);
	if (ans == -1) printf("no solution\n");
	else printf("%d\n", ans);
	return 0;
}

题目:
1.洛谷:P3846 【模板】BSGS
2.洛谷:P2485 计算器

扩展BSGS

当a,p不一定互质时,需要用到扩展BSGS。总体思路:将a,p约分,使其互质,然后用BSGS求解。

d = g c d ( a , p ) d=gcd(a,p) d=gcd(a,p),根据同余性质,下式成立:
a x d ≡ b d ( m o d   p d ) \frac{a^x}{d}\equiv \frac{b}{d} (mod \,\frac{p}{d}) daxdb(moddp)
此时如果 d ∤ b d \nmid b db,要么此时 b = 1 b=1 b=1 x = 0 x=0 x=0,否则根据裴蜀定理可知,上式无解。因此,在最开始的时候判断 b b b是否为1,以后凡是遇到 d ∤ b d \nmid b db的情况,说明无解。
a d   a x − 1 ≡ b d ( m o d   p d ) t = a d b = b d p = p d t   a x − 1 ≡ b ( m o d   p ) \begin{aligned} \frac{a}{d}\,{a^{x-1}} &\equiv \frac{b}{d} (mod \,\frac{p}{d}) \\ t = \frac{a}{d}\quad b &=\frac{b}{d} \quad p = \frac{p}{d} \\ t \,{a^{x-1}} &\equiv b (mod \,p) \\ \end{aligned} daax1t=dabtax1db(moddp)=dbp=dpb(modp)
继续判断此时a,p是否互质,如果不互质,按照上面的方法继续约分。直到互质。得到同余方程:
a k ∏ i = 1 k d i   a x − k ≡ b ∏ i = 1 k d i   ( m o d   p ∏ i = 1 k d i ) \frac{a^k}{\prod _{i=1}^{k}d_i}\, a^{x-k} \equiv \frac{b}{\prod _{i=1}^{k}d_i}\, (mod \, \frac{p}{\prod _{i=1}^{k}d_i}) i=1kdiakaxki=1kdib(modi=1kdip)
化减为:
t   a x 1 ≡ b 1 ( m o d   p 1 ) t\,a^{x1}\equiv b1(mod \, p1) tax1b1(modp1)
按照普通的BSGS解即可。最后求出的答案再加上k。此时求出的答案是 ≥ k \ge k k的,还需要特判 [ 1 , k ] [1,k] [1,k]的解的情况。

while (1)
	{
		scanf("%d%d%d", &a, &p, &b);
		if (!a && !p && !b) break;
		if (b == 1)  // 特判b为1的情况 
		{
			printf("0\n"); continue;  
		}
		int ans = 0, k = 0, d, t = 1;
		a %= p;
		b %= p;
		while (1)
		{
			d = gcd(a, p);
			if (d == 1) break; // 如果a、p互质,按照普通BSGS求解  
			++k;
			if (b % d) // 如果d不能整除b,根据裴蜀定理,无解  
			{
				printf("No Solution\n");
				ans = -1; 				
				break;
			}
			t = (long long) t * (a / d) % p; 
		//	t = a / d * t;  // 这样写有可能溢出,只能过40%的数据  
			b /= d; p /= d;
		}
		// 首先特判[1, k]是否满足 
		if (ans != -1) 
		for (int i = 1; i <= k; ++i)
		{
			if (szpow(a, i, p) == b % p) 
			{
				printf("%d\n", i);
				ans = -1;
				break;
			}
		} 
		if (ans == -1) continue;
		ans = BSGS(a, b, p, t);
		if (ans == -1) printf("No Solution\n");
		else printf("%d\n", ans + k);  // 最终结果还要加k  
	}	

注意事项:在求解BSGS的时候,不要忘记乘上t。

题目:
1.洛谷:P4195 【模板】扩展BSGS

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值