1. 程式人生 > 實用技巧 >BSGS演算法解析

BSGS演算法解析

前置芝士:

1.快速冪(用於求一個數的冪次方)

2.STL裡的map(快速查詢)

詳解

BSGS 演算法適用於解決高次同餘方程 \(a^x\equiv b (mod p)\)

由費馬小定理可得 x <= p-1

我們設 \(m = sqrt(p)\) 至於為什麼寫,下文會講到。

那麼\(x\)就可以用 \(m\) 表示出來。

即 x = \(k \times m - j\)

移項可得 \(a^t \equiv b\times a^j\) 其中 t = \(k \times m\)

這也就是我們為什麼把\(x\)\(k \times m - j\)來表示。

因為改為加\(j\)後,移項後要求逆元,就會變得很麻煩。

這樣,我們就可以列舉每個\(k\)\(j\),來判斷左右兩邊得值是否相等就行了。

首先,我們可以列舉j 將 \(b\times a^j\)放入map中。

然後,從小到大列舉\(k\),在雜湊表中,找到最大的\(j\)滿足 \(a^t \equiv b\times a^j\) 其中 t = \(k \times m\)

若存在\(k\times m -j\)就是方程的解

關於上文中,為什麼要設\(m = sqrt(p)\)

是為了保證BSGS的複雜度,是左右兩邊的數儘可能的均勻。

例題

P3846 [TJOI2007] 可愛的質數

模板題,水過去了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
#define LL long long
int a,b,p;
LL ksm(LL a, LL b)
{
	LL res = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a % p;
		a = a * a % p;
	}
	return res;
}
int BSGS(int a,int b,int p)
{
	map<LL,int> hash;  hash.clear();
	int m = (int)sqrt(p);
	for(int i = 0; i <= m; i++)
	{
		LL val = ksm(a,i) * b % p;//b * a ^ i
		hash[val] = i;//放入map中 
	}
	a = ksm(a,m);//a ^ m
	for(int i = 0; i <= m; i++)
	{
		LL val = ksm(a,i);//(a^m)^i
		int j = hash.find(val) == hash.end() ? -1 : hash[val];//如果沒有j就為-1 
		if(j >= 0 && i * m - j >= 0) return i * m - j;//找到一組解 
	}
	return -1;
}
int main()
{
	scanf("%d%d%d",&p,&a,&b);
	LL ans = BSGS(a,b,p);
	if(ans == -1) cout<<"no solution"<<endl;
	else printf("%lld\n",ans);
	return 0;
}

P2485 [SDOI2011]計算器

一道很多演算法綜合在一起的模板題,水過去了。

但我還沒寫,先把坑占上

ENDING