【数学】Baby Step,Giant Step

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

\[a^x\equiv b\pmod p \]

的非负整数解.

首先,

\[a^0\equiv 1\pmod p \]

又由欧拉定理知

\[a^{\varphi(p)}\equiv 1\pmod p \]

所以出现了周期.

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

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


\(\rm Baby\ Step,Giant\ Step\),简称 \(\rm BSGS\) 算法,俗称大步小步算法或拔山盖世.

\(a,p\) 不互质时就得用 \(\rm exBSGS\) 了.

\(x\) 表示成带余除法的样子:\(x=it+j\),其中 \(t\) 是某个整数(取值待会讨论),那么有 \(0\le i\le\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor,0\le j\le t-1\).

\[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 \]

\(\rm Baby\ Step\)\(\forall j\in [0,t-1]\),将 \((b\cdot a^{-j})\bmod p\) 扔进 \(\rm Hash\) 表.

\(\rm Giant\ Step\)\(\forall i\in\left[0,\left\lfloor \dfrac{\varphi(p)}{t}\right\rfloor\right]\),在 \(\rm Hash\) 表中找是否有 \((a^t)^i\bmod p\),若有,则 \(x=it+j\) 就是其中一个解.

\(\rm Baby\ Step\) 的时间复杂度为 \(\mathcal{O}(t\log t)\)\(\rm Giant\ Step\) 的时间复杂度为 \(\mathcal{O}\left(\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\log t\right)\),所以总的时间复杂度就是 \(\mathcal{O}\left(\max\left(t,\left\lfloor\dfrac{\varphi(p)}{t} \right\rfloor\right)\log t\right)\),这个很像分块的时间,当取 \(t=\left\lfloor\sqrt{\varphi(p)} \right\rfloor\) 时最优.

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

所以我们改为令 \(x=it-j\).


\(x=it-j\),那么有 \(0\le i\le \left\lceil\dfrac{\varphi(p)}{t}\right\rceil\)\(0\le j\le t-1\).

\[a^x\equiv b\pmod p\\ a^{it-j}\equiv b\pmod p\\ (a^t)^i\equiv b\cdot a^j\pmod p \]

\(\rm Baby\ Step\)\(\forall j\in [0,t-1]\),将 \((b\cdot a^{j})\bmod p\) 扔进 \(\rm Hash\) 表.

\(\rm Giant\ Step\)\(\forall i\in\left[0,\left\lceil \dfrac{\varphi(p)}{t}\right\rceil\right]\),在 \(\rm Hash\) 表中找是否有 \((a^t)^i\bmod p\),若有且 \(it-j\ge 0\),则 \(x=it-j\) 就是其中一个解.

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

如果用 \(\rm Hash\)\(\mathcal{O}(\sqrt{p})\),如果用 \(\rm map\)\(\mathcal{O}(\sqrt{p}\log\sqrt{p})\).

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

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

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

update:

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

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

\(\text{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;
}

矩阵 \(\rm BSGS\)

【BZOJ4128】Matrix

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

用矩阵乘法 \(+\) 矩阵 \(\rm Hash\) 即可

\(\text{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;
}
posted @ 2021-11-03 12:25  mango09  阅读(76)  评论(0编辑  收藏  举报
-->