高次同余方程的Baby Step,Giant Step(BSGS)算法

倾然丶 夕夏残阳落幕 提交于 2019-11-29 02:39:27

问题引入

给定整数a,b,pa,b,p,其中b,pb,p互质,求一个非负整数xx,使得axb(modp)a^x \equiv b \pmod{p}

因为加了次数,所以扩展欧几里得算法就无法实现了,所以我们要使用**Baby Step,Giant Step(BSGS)**算法

Baby Step,Giant Step 算法

因为a,pa,p互质,所以可以在模pp的意义下执行关于a的乘、除法运算。
x=itjx=i*t-j,其中t=ceil(p),0<=j<=t1t=ceil(\sqrt{p}),0<=j<=t-1,则方程变为aitjb(modp)a^{i*t-j} \equiv b \pmod{p}。即(at)ibaj(modp)(a^t)^i \equiv b*a^j \pmod p
对于所有的j[0,t]j \in [0,t],计算出(at)imod  p(a^t)^i\mod p,在Hash表中查找是否存在j,跟新答案即可。时间复杂度为O(p)O(\sqrt{p})

参考程序

#include<iostream>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
int power(int a,int b,int c){
	LL ans=1%c;a%=c;
	while(b){
		if(b&1)ans=(LL)ans*a%c;
		a=LL(a)*a%c;b>>=1;
	}
	return ans;
}
int baby_step_giant_step(int a,int b,int p){
	map<int,int>hash;hash.clear();
	b%=p;int t=(int)sqrt(p)+1;
	for(int j=0;j<t;j++){
		int val=(LL)b*power(a,j,p)%p;//b*a^j
		hash[val]=j;
	}
	a=power(a,t,p);//a^t
	if(!a)return !b?1:-1;
	for(int i=0;i<=t;i++){
		int val=power(a,i,p);//(a^t)^i
		int j=hash.find(val)==hash.end()?-1:hash[val];
		if(j>=0&&i*t-j>=0)return i*t-j;
	}
	return -1;
}
int main(){
	int c,a,b;
	while(~scanf("%d%d%d",&c,&a,&b)){
		int ans=baby_step_giant_step(a,b,c);
		if(ans==-1)puts("no solution!");
		else printf("%d\n",ans);
	}
	return 0;
}

如果要求最小正整数解,就加一个minn,算一下即可

参考代码

#include<iostream>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
int power(int a,int b,int c){
	LL ans=1%c;a%=c;
	while(b){
		if(b&1)ans=(LL)ans*a%c;
		a=LL(a)*a%c;b>>=1;
	}
	return ans;
}
int baby_step_giant_step(int a,int b,int p){
	map<int,int>hash;hash.clear();
	b%=p;int t=(int)sqrt(p)+1;
	for(int j=0;j<t;j++){
		int val=(LL)b*power(a,j,p)%p;//b*a^j
		hash[val]=j;
	}
	a=power(a,t,p);//a^t
	if(!a)return !b?1:-1;
	int minn=-1;
	for(int i=0;i<=t;i++){
		int val=power(a,i,p);//(a^t)^i
		int j=hash.find(val)==hash.end()?-1:hash[val];
		if(j>=0&&i*t-j>=0){
			if(minn==-1)minn=i*t-j;
			else minn=min(minn,i*t-j);
		}
	}
	return minn;
}
int main(){
	int c,a,b;
	while(~scanf("%d%d%d",&c,&a,&b)){
		int ans=baby_step_giant_step(a,b,c);
		if(ans==-1)puts("no solution!");
		else printf("%d\n",ans);
	}
	return 0;
}

细心的读者肯定能发现,这里没有用Hash而是用了map,其实map相对于Hash会慢一些(多一个log),但不影响复杂度,能够让代码简洁易懂。下面我们来一个用Hash的
参考代码

#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long LL;
int power(int a,int b,int c){
	LL ans=1%c;a%=c;
	while(b){
		if(b&1)ans=(LL)ans*a%c;
		a=LL(a)*a%c;b>>=1;
	}
	return ans;
}
struct Hash{
	int x,val,nxt;
}hsh[210000];int cnt,last[210000];
void add(int x,int val){
	int now=x%200011;
	hsh[++cnt]=(Hash){x,val,last[now]};
	last[now]=cnt;
}
int find(int x){
	int now=x%200011;
	for(int i=last[now];i;i=hsh[i].nxt)
		if(hsh[i].x==x)return hsh[i].val;
	return -1;
}
int baby_step_giant_step(int a,int b,int p){
	cnt=0;memset(last,0,sizeof(last));
	b%=p;int t=(int)sqrt(p)+1;
	for(int j=0;j<t;j++){
		int val=(LL)b*power(a,j,p)%p;
		add(val,j);
	}
	a=power(a,t,p);
	if(!a)return !b?1:-1;
	int minn=-1;
	for(int i=0;i<=t;i++){
		int val=power(a,i,p);
		int j=find(val);
		if(j>=0&&i*t-j>=0){
			if(minn==-1)minn=i*t-j;
			else minn=min(minn,i*t-j);
		}
	}
	return minn;
}
int main(){
	int c,a,b;
	while(~scanf("%d%d%d",&c,&a,&b)){
		int ans=baby_step_giant_step(a,b,c);
		if(ans==-1)puts("no solution!");
		else printf("%d\n",ans);
	}
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!