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

問題引入

給定整數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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章