离散对数(数论-BSGS算法)

Description
  在初等代数里关于对数:ax=b,则x=loga(b),即x为以a为底b的对数。
  在模算术里也有类似的概念:ax≡b(mod n),则x为以a关于模n的b的对数。
  在这里我们只考虑n为质数的简单情况:给定a,b,n,请你计算ax≡b(mod n)的最小非负整数解。
Input
  多组数据:每组数据包含三个整数:a,b,n,其中n一定是一个质数。
Output

  每组数据输出一行,表示解,如果不存在,则输出-1。


分析:

对于给出的方程                                   a^x≡b(mod n)

可以把x分解为i*m-j:                          a^(i*m-j)≡b(mod n)

同乘a^j:                                           a^(i*m)≡b*a^j(mod n)

令m=√n,j∈[0,m],那么先一遍 j=0~m,求出 b*a^j  mod  n 的结果,j 存入哈希表中。

然后一遍 i=1~m,求出a^(i*m) mod n的结果,并在哈希表中查找是否有结果相同的 j,如果找到,根据上面的式子,x=i*m-j。此时的x一定是最小的。

否则,返回-1,无解。

于是在√n的时间内求出了答案。

有其他版本的做法,但很多是i*m+j,这样需要求逆元。i*m-j的做法避免了求逆元。

扫描二维码关注公众号,回复: 2167339 查看本文章


注意:

1,哈希表的大小,大概开到2*m就可以。

2,j和i的范围,一点都不能错。


#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=87719;
int last[MAXN],np;
struct data{LL num,id;int pre;}Hash[MAXN];

void insert(LL num,LL id)
{
	LL hs=num%MAXN;
	Hash[++np]=(data){num,id,last[hs]};
	last[hs]=np;
}

LL find(LL num)
{
	LL hs=num%MAXN;
	for(int p=last[hs];p;p=Hash[p].pre)
	{
		if(Hash[p].num==num)
			return Hash[p].id;
	}
	return -1;
}

LL qkpow(LL a,LL b,LL n)
{
	LL s=1;
	while(b)
	{
		if(b&1) s=(s*a)%n;
		a=(a*a)%n;
		b=b>>1;
	}
	return s;
}

LL bsgs(LL a,LL b,LL n)
{
	memset(last,0,sizeof(last));
	memset(Hash,0,sizeof(Hash));
	np=0;
	
	LL m=ceil(sqrt(n));
	LL aa=qkpow(a,m,n);
	LL i;
	LL e=b%n;
	
	for(i=0;i<=m;i++)  
	{
		insert(e,i);
		e=(e*a)%n;
	}
	
	e=1;
	for(i=1;i<=m;i++) // 每次乘a^m 
	{
		e=(e*aa)%n;
		if(find(e)>=0) //如果hash表里找到了 
		{
			LL ans=i*m-find(e);  //i*m-j 
			return (ans%n+n)%n;
		}
	}
	
	return -1;
}

int main()
{
//	freopen("in.txt","r",stdin);
	LL a,b,n;
	while(scanf("%lld",&a)==1)
	{
		scanf("%lld%lld",&b,&n);
		LL ans=bsgs(a,b,n);
		printf("%lld\n",ans);
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/wwwengine/article/details/81014883