Loading

BSGS算法(Baby Step Giant Step)

引子

我们之前学习了如何解这种方程\(x\equiv b_1\pmod{m_1}\)

我们可以用扩展中国剩余定理或者欧拉定理

正文

问题

求最小的\(x\)使得\(a^x\equiv b\pmod{m},\gcd(a,m)=1\)

在没有学过BSGS北上广深之前,我们用暴力循环可以解决这一个问题。

时间复杂度\(O(\varphi(m))\)(提示:模的循环节)

但是如果\(m\)为质数的话\(\varphi(m)=m-1\)时间复杂度很高。

BSGS就是用来解决高次同余方程的。

解法

BSGS本质上就是一个优化后的暴力算法

我们构造一个\(x=At-B\),那么\(a^x=a^{At-B}\equiv b\pmod{m}\)

于是我们移项一下得到\(a^{At}\equiv ba^B\pmod{m}\)

求出\(A=\lceil\frac{x}{t}\rceil,B=x\%t\),推出\(B\in[0,t),A\in[0,\lceil\frac{x}{t}\rceil)\)

此时我们先预处理出\(ba^B\)的所有值,在一一比对。

此时我们构造\(t=\sqrt{\varphi(m)}\)使得时间复杂度为\(O(\sqrt{m})\)

在代码中我们直接取\(t=\sqrt(m)+1\)就可以了,身去了求\(\varphi\)的时间。

注意\(\gcd(a,m)=1\)

代码

P3846 [TJOI2007] 可爱的质数/【模板】BSGS

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll qpow(ll a, ll n, ll p) //快速幂
{
    ll ans = 1;
    a %= p;
    while(n)
    {
        if(n & 1)
            ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

ll BSGS(ll a, ll b, ll m)
{
    //unordered_map<ll, ll> hs; //unordered_map是一个STL自带的哈希表 
    map<ll, ll> hs; //map 多一个 log 
    ll cur = b * a % m, t = sqrt(m) + 1; //向上取整 
    for (int B = 1; B <= t; B++)
    {
        hs[cur] = B; // 哈希表存B的值
        cur = cur * a % m;
    }
    ll val = qpow(a, t, m);
	cur = val; // a^t
    for(int A = 1; A <= t; A++)
    {
        if(hs[cur])
            return A * t - hs[cur];
        cur = (cur * val) % m; //再乘一个a^t次方 
    }
    return -1; // 无解
}

int main()
{
	int p, b, n;
	cin >> p >> b >> n;
	int res = BSGS(b, n, p);
	if(res == -1)
		cout << "no solution";
	else
		cout << res;
	return 0;
} 

P2485 [SDOI2011]计算器

唯一要注意的是我们要特判\(y,p\)不互质的情况。

#include<bits/stdc++.h>

using namespace std;

#define int long long

int T , K , y , z , p , ans;

inline int quickly_pow(int x , int y , int z){
	int res = 1;
	while(y != 0){
		if(y % 2 == 1){
			res = res * x % z;
		}
		x = x * x % z;
		y >>= 1;
	}
	return res;
}

inline int BSGS(int a , int b , int m){
	unordered_map <int , int> Hash;
	int cur = a * b % m , t = sqrt(m) + 1;
	for(int B = 1 ; B <= t ; B++ ){
		Hash[cur] = B;
		cur = cur * a % m;
	} 
	int val = quickly_pow(a , t , m);
	cur = val;
	for(int A = 1 ; A <= t ; A++ ){
		if(Hash[cur] != 0){
			return A * t - Hash[cur]; 
		}
		cur = (cur * val) % m;
 	}
 	return -1;
}

inline int read(){
	int s = 0 , w = 1;
	char ch = getchar();
	while((ch < '0') || (ch > '9')){
		if(ch == '-'){
			w = -1;
		}
		ch = getchar();
    }
   	while((ch >= '0') && (ch <= '9')){
   		s = (s << 3) + (s << 1) + ch - '0';
		ch = getchar();
	}
   	return s * w;
}

inline void write(int x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9){
    	write(x / 10);
	}
    putchar(x % 10 + '0');
    return ;
}

signed main(){
	
	T = read();
	K = read();
	
	if(K == 1){
		for(int i = 1 ; i <= T ; i++ ){
			y = read();
			z = read();
			p = read();
			printf("%lld\n" , quickly_pow(y , z , p));
		}
	}
	
	if(K == 2){
		for(int i = 1 ; i <= T ; i++ ){
			y = read();
			z = read();
			p = read();
			y %= p;
			if(y == 0){
				cout << "Orz, I cannot find x!" << endl;
			}
			else{
	            printf("%lld\n" , quickly_pow(y , p - 2 , p) * z % p);
			}
		}
	}
	
	if(K == 3){
		for(int i = 1 ; i <= T ; i++ ){
			y = read();
			z = read();
			p = read();
			y %= p;
			if(y == 0){
				cout << "Orz, I cannot find x!" << endl;
			}
			else{
				ans = BSGS(y , z , p);
				if(ans == -1){
					cout << "Orz, I cannot find x!" << endl;
				}
				else{
					cout << ans << endl;
				}
			}
		}
	}

	return 0;
}

扩展

如果是\(a^x\equiv b\pmod{m}\)\(a,m\)不互质了呢?

我们将\(a\)写成\(a=a_1\times g\)\(m=m_1\times g\)

同时将原方程转化成不定方程\(a\times a^{x-1}+my=b\)

因为\(a,m,b\)中都有\(g\)就可以转化为\((a/g)\times a^{x-1}+(m/g)y=b/g\)

我们又把它变成一个同余方程\((a/g)a^{x-1}\equiv b/g\pmod{m/g}\)

这样反复迭代就可以了

P4195 【模板】扩展BSGS

核心代码


ll BSGS(ll a, ll b, ll m, ll k = 1) // 带系数k的BSGS,默认为1 
{
   //unordered_map<ll, ll> hs; //unordered_map是一个STL自带的哈希表 
    map<ll, ll> hs; //map 多一个 log 
    ll cur = b * a % m, t = sqrt(m) + 1; //向上取整 
    for (int B = 1; B <= t; B++)
    {
        hs[cur] = B; // 哈希表存B的值
        cur = cur * a % m;
    }
    ll val = qpow(a, t, m);
	cur = val * k % m; // a^t
    for(int A = 1; A <= t; A++)
    {
        if(hs[cur])
            return A * t - hs[cur];
        cur = (cur * val) % m; //再乘一个a^t次方 
    }
    return -1000; //无解,返回一个小一点的负数,确保多次加1后仍然是负数 
}

ll exBSGS(ll a, ll b, ll m, ll k = 1)
{
    if(b == 1) // 特判1,x>0, 所以b==1,则x=0 
        return 0;
    if(a == 0 && b == 0) // 特判2,底数为0,最小的x=1, 因为0^0 = 1 
        return 1;
    ll d = gcd(a, m);
    if(b % d != 0) // 无解, 斐蜀定理 
        return -1000;
    else if(d == 1)
        return BSGS(a, b, m, k % m); // 递归结束 
    return exBSGS(a, b / d, m / d, k * a / d % m) + 1; // 递归
}
posted @ 2021-03-14 19:40  zhangwenxuan  阅读(115)  评论(0编辑  收藏  举报