【学习笔记】Baby Step Giant Step 算法

\(Baby \ Step \ Giant \ Step\)

这个算法分为:狭义\(Bsgs\rightarrow exBsgs \rightarrow\) 广义 \(Bsgs\)

狭义BSGS

引入

求:满足一个最小的\(x\),满足:

\[A^x\equiv B(mod \ p) \]

其中 \(A,B\) 给定,\(p\in \{\varphi(x)=x-1\}\)

过程

首先一个结论:我们取出\(A^0\rightarrow A^{p-1}\) 必然有一个 \(A^q\equiv B(mod\ p)\)

如果你不知道抽屉原理……(其实就是个同余系下的一通操作)

但是我们发现并不能这样,因为时间不允许

然后我们这样操作一发:

任意给个 \(S\) 求出来:$B \times A^1\rightarrow B \times A^{S-1} \ mod \ p $ 的值,放到一个 \(hash\) 表中

然后我们再求:\(A^{0\times S} \rightarrow A^{\lfloor\frac{p}{S}\rfloor\times S}\)

对于每个 \(A^S\) 的次幂,我们移项,转化成下式:

\[A^{k \times S}\equiv {B \times {A^ p}} \]

然后我们把它扔到我们原来的 \(hash\) 表里面找

如果找到了,答案就是 \(k \times S-p\)

复杂度分析:

默认我们的 \(hash\) 是个 \(O(1)\)

那么复杂度是个\(O(S+\frac {mod} S )\)

这个是个对勾函数对吧,显而易见的在 \(S= \sqrt P\) 时我们可以得到该函数的最小值

Code

考虑到我不会写正常的\(hash\) ,我选择了 \(map\)

	mp.clear(); int s=ceil(sqrt(p)),t=z%p; mp[t]=0;
	for(int i=1;i<=s;++i)
	{
		t=t*y%p; mp[t]=i;
	}
	int base=ksm(y,s,p),now=1; bool fl=0;
	for(int i=1;i<=s;i++)
	{
		now*=base; now%=p;
		if(!mp.count(now)) continue;
		int t=i*s-mp[now]; 
		printf("%lld\n",(t%p+p)%p); fl=1; break;
	} 

拓展 \(BSGS\)

引入

如果 \(p\) 不是质数了呢?

过程

可能在乘法的过程中把质因子凑全了

先考虑 \(B=0\) 的情况

换一下式子发现:

\[k=\frac {A^{x}} p \]

其中 \(k\) 为整数

那我们枚举 \(x\) 同时在每一步除掉 \(gcd(A^x,p)\)

如果什么时候 \(p=1\)\(x\) 为最小解

这里如果 \(gcd(A,p)=1\) 那么无解

然后是正常情况:

\[A^x\equiv B (mod \ p) \]

然后推一发有:

\[A^{x-1}\times A \equiv B (mod\ p) \]

\[A^{x-1}\times A-kp=b \]

\(k\) 是任意的一个整数

由裴蜀定理:

如果 \(B\%gcd(A,p)\) 无解

然后我们把等式两边除一下 \(gcd(A,p)\)

则有:

\[\frac{A}{gcd(A,p)} \times A^x \equiv \frac B {gcd(B,p)} (mod \ \frac{p}{gcd(A,p)}) \]

这个就是可以拿普通的 \(BSGS\) 做了对吧

然后就没了

板子题在 \(Luogu\) 上有

Code

inline void work()
{
	y%=p; z%=p;
	if(z==1) return puts("0"),void();
	if(!y&&!z) return puts("1"),void();
	if(!y) return puts("No Solution"),void();
	if(!z)
	{
		int res=0,d;
		while((d=__gcd(y,p))!=1)
		{
			++res; p/=d;
			if(p==1) return printf("%lld\n",res),void();
		}return puts("No Solution"),void();
	}
	int c=1,res=0,d;
	while((d=__gcd(y,p))!=1)
	{
		if(z%d) return puts("No Solution"),void();
		p/=d; z/=d; res++; (c*=y/d)%=p;
		if(c==z) return printf("%lld\n",res),void();
	}
	mp.clear(); int t=z%p,s=ceil(sqrt(p)); mp[t]=0;
	for(int i=1;i<=s;++i) (t*=y)%=p,mp[t]=i;
	int base=ksm(y,s,p),now=c;
	for(int i=1;i<=s;++i)
	{
		(now*=base)%=p;
		if(!mp.count(now)) continue;
		return printf("%lld\n",i*s+res-mp[now]),void();
	} puts("No Solution"); 
	return ;
}

广义BSGS

形式:\(f(f(f(f(x))))=k(mod\ p)\)

狭义的普通 \(bsgs\) 对应就是 \(f(x)=x\times A\)

如果我们把 \(f(x)\) 看成矩阵什么的也一样能做

我们发现其实在 \(bsgs\) 中存在一部求逆函数的操作,其实就是逆元

然后我们对于要求的\(f(x)\),找到相应的逆函数即可

逆函数可以是逆矩阵,向量的逆啥的

应用是求广义斐波那契数列的循环节

例题:\(bzoj4128\)

矩阵求逆之后上广义bsgs即可,板子题

然而我们可以直接偷懒换式子:

\[A^{kS}=B\times A^t(mod\ p) \]

所以我们把右边的所有结果存一存,左边就上矩阵乘就好了,并不用求逆

对于判定矩阵相等,我们 \(hash\)

Code

这份代码被卡了常,但是我不想改了

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return  res*f;
	}
	#define ull unsigned long long
	const int N=80,b1=998244353,b2=1e9+7;
	int n,p;
	struct mat{
		int a[N][N];
		ull v1,v2;
		inline void init()
		{
			for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=read();
			return ;
		}
		mat operator * (const mat x) const
		{
			mat ans; memset(ans.a,0,sizeof(ans.a));
			for(int i=1;i<=n;++i)
			{
				for(int j=1;j<=n;++j) 
				{
					for(int k=1;k<=n;++k)
					{
						ans.a[i][j]+=a[i][k]*x.a[k][j]%p; ans.a[i][j]%=p;
					}
				}
			}return ans;
		}
		inline void hash()
		{
			for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) v1=v1*b1+a[i][j],v2=v2*b2+a[i][j];
			return ;
		}
	}t,now,y,z;
	map<pair<ull,ull>,int> mp;
	signed main()
	{
		n=read(),p=read();
		y.init(); z.init();
		for(int i=1;i<=n;++i) now.a[i][i]=t.a[i][i]=1;
		int s=ceil(sqrt(p)); 
		for(int i=1;i<=s;++i) t=t*y,z=z*y,z.hash(),mp[make_pair(z.v1,z.v2)]=i;
		for(int i=1;i<=s;++i)
		{
			now=now*t; now.hash(); pair<ull,ull> tmp;
			tmp=make_pair(now.v1,now.v2);
			if(!mp.count(tmp)) continue;
			printf("%lld\n",i*s-mp[tmp]); 
		} 
		return 0;
	}
}
signed main(){return yspm::main();}

猜你喜欢

转载自www.cnblogs.com/yspm/p/12819376.html
今日推荐