【数学】Baby Step,Giant Step

给定整数 a , b , p a,b,p a,b,p a , p a,p a,p 互质,请求出高次同余方程

a x ≡ b ( m o d p ) a^x\equiv b\pmod p axb(modp)

的非负整数解.

首先,

a 0 ≡ 1 ( m o d p ) a^0\equiv 1\pmod p a01(modp)

又由欧拉定理知

a φ ( p ) ≡ 1 ( m o d p ) a^{\varphi(p)}\equiv 1\pmod p aφ(p)1(modp)

所以出现了周期.

所以 x x x 在区间 [ 0 , φ ( p ) ) [0,\varphi(p)) [0,φ(p)) 中一定有,否则就是无解.

那么暴力就是遍历 0 ∼ φ ( p ) − 1 0\sim \varphi(p)-1 0φ(p)1 去试每一个,时间复杂度为 O ( φ ( p ) ) \mathcal{O}(\varphi(p)) O(φ(p)).


B a b y   S t e p , G i a n t   S t e p \rm Baby\ Step,Giant\ Step Baby Step,Giant Step,简称 B S G S \rm BSGS BSGS 算法,俗称大步小步算法或拔山盖世.

a , p a,p a,p 不互质时就得用 e x B S G S \rm exBSGS exBSGS 了.

x x x 表示成带余除法的样子: x = i t + j x=it+j x=it+j,其中 t t t 是某个整数(取值待会讨论),那么有 0 ≤ i ≤ ⌊ φ ( p ) t ⌋ , 0 ≤ j ≤ t − 1 0\le i\le\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor,0\le j\le t-1 0itφ(p),0jt1.
a x ≡ b ( m o d p ) a i t + j ≡ b ( m o d p ) a i t ≡ b ⋅ a − j ( m o d p ) ( a t ) i ≡ b ⋅ a − j ( m o d p ) a^x\equiv b\pmod p\\ a^{it+j}\equiv b\pmod p\\ a^{it}\equiv b\cdot a^{-j}\pmod p\\ (a^t)^i\equiv b\cdot a^{-j}\pmod p axb(modp)ait+jb(modp)aitbaj(modp)(at)ibaj(modp)
B a b y   S t e p \rm Baby\ Step Baby Step ∀ j ∈ [ 0 , t − 1 ] \forall j\in [0,t-1] j[0,t1],将 ( b ⋅ a − j )   m o d   p (b\cdot a^{-j})\bmod p (baj)modp 扔进 H a s h \rm Hash Hash 表.

G i a n t   S t e p \rm Giant\ Step Giant Step ∀ i ∈ [ 0 , ⌊ φ ( p ) t ⌋ ] \forall i\in\left[0,\left\lfloor \dfrac{\varphi(p)}{t}\right\rfloor\right] i[0,tφ(p)],在 H a s h \rm Hash Hash 表中找是否有 ( a t ) i   m o d   p (a^t)^i\bmod p (at)imodp,若有,则 x = i t + j x=it+j x=it+j 就是其中一个解.

B a b y   S t e p \rm Baby\ Step Baby Step 的时间复杂度为 O ( t log ⁡ t ) \mathcal{O}(t\log t) O(tlogt) G i a n t   S t e p \rm Giant\ Step Giant Step 的时间复杂度为 O ( ⌊ φ ( p ) t ⌋ log ⁡ t ) \mathcal{O}\left(\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\log t\right) O(tφ(p)logt),所以总的时间复杂度就是 O ( max ⁡ ( t , ⌊ φ ( p ) t ⌋ ) log ⁡ t ) \mathcal{O}\left(\max\left(t,\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\right)\log t\right) O(max(t,tφ(p))logt),这个很像分块的时间,当取 t = ⌊ φ ( p ) ⌋ t=\left\lfloor\sqrt{\varphi(p)} \right\rfloor t=φ(p) 时最优.

但是有一个问题,上式右边是 b ⋅ a − j b\cdot a^{-j} baj,导致需要处理逆元,肥肠麻烦.

所以我们改为令 x = i t − j x=it-j x=itj.


x = i t − j x=it-j x=itj,那么有 0 ≤ i ≤ ⌈ φ ( p ) t ⌉ 0\le i\le \left\lceil\dfrac{\varphi(p)}{t}\right\rceil 0itφ(p) 0 ≤ j ≤ t − 1 0\le j\le t-1 0jt1.
a x ≡ b ( m o d p ) a i t − j ≡ b ( m o d p ) ( a t ) i ≡ b ⋅ a j ( m o d p ) a^x\equiv b\pmod p\\ a^{it-j}\equiv b\pmod p\\ (a^t)^i\equiv b\cdot a^j\pmod p axb(modp)aitjb(modp)(at)ibaj(modp)
B a b y   S t e p \rm Baby\ Step Baby Step ∀ j ∈ [ 0 , t − 1 ] \forall j\in [0,t-1] j[0,t1],将 ( b ⋅ a j )   m o d   p (b\cdot a^{j})\bmod p (baj)modp 扔进 H a s h \rm Hash Hash 表.

G i a n t   S t e p \rm Giant\ Step Giant Step ∀ i ∈ [ 0 , ⌈ φ ( p ) t ⌉ ] \forall i\in\left[0,\left\lceil \dfrac{\varphi(p)}{t}\right\rceil\right] i[0,tφ(p)],在 H a s h \rm Hash Hash 表中找是否有 ( a t ) i   m o d   p (a^t)^i\bmod p (at)imodp,若有且 i t − j ≥ 0 it-j\ge 0 itj0,则 x = i t − j x=it-j x=itj 就是其中一个解.

总的时间复杂度是 O ( max ⁡ ( t , ⌈ φ ( p ) t ⌉ ) log ⁡ t ) \mathcal{O}\left(\max\left(t,\left\lceil\dfrac{\varphi(p)}{t} \right\rceil\right)\log t\right) O(max(t,tφ(p))logt),当取 t = ⌈ φ ( p ) ⌉ t=\left\lceil\sqrt{\varphi(p)} \right\rceil t=φ(p) 时最优.

如果用 H a s h \rm Hash Hash O ( p ) \mathcal{O}(\sqrt{p}) O(p ),如果用 m a p \rm map map O ( p log ⁡ p ) \mathcal{O}(\sqrt{p}\log\sqrt{p}) O(p logp ).

你会发现它和求单个数逆元一样,时间都只与模数有关,很有意思.

P3846 [TJOI2007] 可爱的质数/【模板】BSGS

对于本题,要求出最小解,因为 0 ≤ j ≤ t − 1 0\le j\le t-1 0jt1,所以要使 x x x 最小只需使 i i i 最小,按顺序遍历,第一次找到就返回.

update:

int val = (ll)b * aj % p;
hash[val] = j;

有可能存在两个 j j j 使得 b ⋅ a j   m o d   p b\cdot a^j\bmod p bajmodp 相等,但因为我们是按顺序遍历,所以一定是大的覆盖小的,就会使得 i t − j it-j itj 更小,这正是题目要求的。所以不用担心哈希冲突的问题。

Code \text{Code} Code

注意:在查找时最好不要用

int j = hash[ai] - 1;
if (j >= 0 && i * t - j >= 0)
{
    return i * t - j;
}

因为在查找不到时会新建一个,导致浪费大量时间.

#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
typedef long long ll;
using namespace std;

int phi(int p)
{
	int ans = p;
	for (int i = 2; (ll)i * i <= (ll)p; i++)
	{
		if (p % i == 0)
		{
			ans = ans / i * (i - 1);
			while (p % i == 0)
			{
				p /= i;
			}
		}
	}
	if (p != 1)
	{
		ans = ans / p * (p - 1);
	}
	return ans;
}

int bsgs(int a, int b, int p)
{
	b %= p;
	map<int, int> hash;
	int t = ceil(sqrt(phi(p))), aj = 1;
	for (int j = 0; j < t; j++)
	{
		int val = (ll)b * aj % p;
		hash[val] = j;
		aj = (ll)aj * a % p;
	}
	a = aj;
	int ai = 1;
	for (int i = 0; i <= t; i++)
	{
		if (hash.find(ai) != hash.end())
		{
			int j = hash[ai];
			if (i * t - j >= 0)
			{
				return i * t - j;
			}
		}
		ai = (ll)ai * a % p;
	}
	return -1;
}

int main()
{
	int a, b, p;
	scanf("%d%d%d", &p, &a, &b);
	int res = bsgs(a, b, p);
	if (res == -1)
	{
		puts("no solution");
	}
	else
	{
		printf("%d\n", res);
	}
	return 0;
}

矩阵 B S G S \rm BSGS BSGS

【BZOJ4128】Matrix

给定矩阵 A , B A,B A,B 和模数 P P P,求最小的 x x x,满足 A x ≡ B ( m o d P ) A^x\equiv B\pmod P AxB(modP).

用矩阵乘法 + + + 矩阵 H a s h \rm Hash Hash 即可

Code \text{Code} Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <cmath>
using namespace std;

const int MAXN = 75;
const int BASE = 233;
const int MOD = 19260817;

int n, p;

struct matrix
{
	int a[MAXN][MAXN];
	void init()
	{
		memset(a, 0, sizeof(a));
	}
	void build()
	{
		for (int i = 1; i <= n; i++)
		{
			a[i][i] = 1;
		}
	}
	matrix operator *(const matrix &x)const
	{
		matrix res;
		res.init();
		for (int k = 1; k <= n; k++)
		{
			for (int i = 1; i <= n; i++)
			{
				if (!a[i][k])
				{
					continue;
				}
				for (int j = 1; j <= n; j++)
				{
					res.a[i][j] = (res.a[i][j] + a[i][k] * x.a[k][j]) % p;
				}
			}
		}
		return res;
	}
	int hash()
	{
		int ans = 0;
		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				ans = (ans * BASE + a[i][j]) % MOD;
			}
		}
		return ans;
	}
};

int bsgs(matrix a, matrix b)
{
	map<int, int> hash;
	int t = ceil(sqrt(p));
	matrix aj;
	aj.init(), aj.build();
	for (int j = 0; j < t; j++)
	{
		matrix val = b * aj;
		hash[val.hash()] = j;
		aj = aj * a;
	}
	a = aj;
	matrix ai;
	ai.init(), ai.build();
	for (int i = 0; i <= t; i++)
	{
		int x = ai.hash();
		if (hash.find(x) != hash.end())
		{
			int j = hash[x];
			if (i * t - j >= 0)
			{
				return i * t - j;
			}
		}
		ai = ai * a;
	}
}

int main()
{
	scanf("%d%d", &n, &p);
	matrix a;
	a.init();
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			scanf("%d", &a.a[i][j]);
		}
	}
	matrix b;
	b.init();
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			scanf("%d", &b.a[i][j]);
		}
	}
	printf("%d\n", bsgs(a, b));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值