POJ - 2417 Discrete Logging(bsgs)(费马小定理)

题干:

给定一个素数P, 2<=P< 2 31 2^{31} ,两个正整数B , 2 <=B<P; N , 1<=N<P;
求一个正整数L,使得 B L N ( m o d P ) B^L≡N (mod P)

思路:

本体是一个BSGS模板题。
先来看下需要了解的定理。
因为给定的P为素数,根据费马小定理:
B ( P 1 ) B^{(P-1)} ≡ 1 (mod P)
根据逆元的定义:
a*b ≡ 1 (mod P),则b就是a在模除P的情况下的逆元。
B的逆元可以由费马小定理推出: B ( P 2 ) B^{(P-2)} * B ≡ 1 (mod P)

BSGS的思路是:设m=ceil(sqrt(P ))
令L=i*m+j, 即 B i m + j N ( m o d P ) B^{i*m+j}≡N (mod P)

变形为 B j B i m N ( m o d P ) B^{j}*B^{i*m}≡N (mod P) = > B j N / B i m ( m o d P ) B^{j}≡N/B^{i*m} (mod P)
因为模运算不能有除法,所以这里用 B i m B^{i*m} 在模P的逆元 即 B P i m 1 B^{P-i*m-1}
然后将从0到m的所有 B j B^{j} 存进map中,再遍历从0到m的所有 B P i m 1 B^{P-i*m-1}
如果 B j B^{j} == B P i m 1 B^{P-i*m-1} ,则 L=i*m+j;如果找不到则无解。

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

ll qc(ll a,ll b,ll c)
{
	ll ans=1;
	while(b)
	{
		if(b&1)
			ans=(ans*a)%c;
		a=(a*a)%c;
		b>>=1;
	} 
	return ans%c;
}
ll bsgs(ll a,ll b,ll c)
{
	ll m=ceil(sqrt(c*1.0));
	ll t=qc(a,c-m-1,c),y=1;  //求逆元
	//printf("%lld\n",m);
	map<ll,ll>x;
	x[1]=m;
	for(int i=1;i<m;i++){
		y=(y*a)%c;
		if(!x[y])
			x[y]=i;
	}
	for(int i=0;i<m;i++){
		if(x[b]) //查找map表
		{
			int pos=x[b];
			x.clear();
			return m*i+(pos==m?0:pos);
		}
		b=(b%c*t%c)%c;
	}
	return -1;
}
int main()
{
	ll a,b,c;
	while(scanf("%lld%lld%lld",&c,&a,&b)!=EOF)
	{
		//printf("%lld %lld %lld\n",a,b,c);
		ll ans=bsgs(a,b,c);
		if(ans==-1)
			printf("no solution\n");
		else
			printf("%lld\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42279796/article/details/88966698