1. 程式人生 > >Shank's Baby-Step-Giant-Step Algorithm(BSGS,大步小步演算法)

Shank's Baby-Step-Giant-Step Algorithm(BSGS,大步小步演算法)

簡介

大步小步演算法用於解決離散對數問題。即求模方程axb(mod  p)a^x\equiv b(\mod p)的最小解(或者給出無解)。

先討論pp奇素數的情況。

暴力求解?

列舉xx,判斷axa^x是否與bbpp同餘即可。
但是無解的情況如何判定?
實際上,若x<px<p的情況都無解,即可判斷此方程無解。理由如下:
我們知道:因為(a,p)=1(a,p)=1,根據尤拉定理:aφ(p)1(mod  p

)a^{\varphi(p)}\equiv 1 (\mod p)由於φ(p)=p1\varphi(p)=p-1,所以ap11(mod  p)a^{p-1}\equiv 1(\mod p),所以當xx0,1,2...p10,1,2...p-1時,axa^x至少會形成一個迴圈節。
時間複雜度O(p)O(p)

BSGS演算法

m=qm=\left \lceil \sqrt{q} \right \rceil

  1. 先算出所有的a
    x,xma^x,x\leqslant m
    ,判斷是否有解,並把axa^x的值扔進HashHash表裡。
  2. 若無解,則考慮x>mx>m
    可以把xx拆成x=i×m+j(j<m)x=i\times m + j(j<m)的形式。
    那麼:
    ax=ai×m+j=ami×ajb(mod  p)a^x=a^{i\times m + j}=a^{{m}^{i}}\times a^j\equiv b(\mod p)
    移項得:
    ajb×ami(mod  p)a^j\equiv b\times a^{{-m}^{i}}(\mod p)
    那麼先預處理出ama^{m}的逆元,再列舉ii,判斷aja^j是否存在,若存在,則答案為i×m+ji\times m+j
    時間複雜度為O(p)O(\sqrt{p})
    (如果pp不是奇素數,但滿足(a,p)=1(a,p)=1呢?)
    mm變為m=φ(p)m=\sqrt{\varphi(p)}
    φ(p)\varphi(p)的時間複雜度是O(p)O(\sqrt{p}),所以複雜度還是沒有變。。。
    還是老老實實取m=pm=\sqrt{p}吧。
    時間複雜度O(p)O(\sqrt{p})
    模板題
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
#define LL long long
int read()
{
	int x = 0; char ch = getchar();
	for(;ch<'0'||ch>'9';ch = getchar());
	for(;ch>='0'&&ch<='9';ch = getchar()) x = (x<<3)+(x<<1)+(ch^48);
	return x;
}
LL Pow(LL x,int a,LL p)
{
	if(a == 0) return 1;
	if(a == 1) return x;
	LL y = Pow(x,a>>1,p);
	y = (y * y)%p;
	if(a & 1) y = (y * x)%p;
	return y; 
}
#define hash_mod 10000037
int head[hash_mod],ne[hash_mod],ha[hash_mod],val[hash_mod],tot;
void hash_insert(int x,int pos)
{
	int y = x % hash_mod;
	for(int i=head[y];i;i=ne[i])
		if(ha[i] == x)
			return;
	ha[++tot] = x;
	val[tot] = pos;
	ne[tot] = head[y];
	head[y] = tot;
}
int hash_find(int x)
{
	int y = x % hash_mod;
	for(int i=head[y];i;i=ne[i])
		if(ha[i] == x)
			return val[i];
	return -1;
}
int BSGS(int a,int b,int p)//a^x=b(mod p) 
{
	int m = ceil(sqrt(p));
	int num = 1;
	for(int i=0;i<=m;i++)
	{
		hash_insert(num,i);
		if(num == b) return i;
		num = (1ll*num * a)%p;
	}
	int inv = Pow(Pow(a,m,p),p-2,p);
	for(int i=1;i<=m;i++)
	{
		b = (1ll*b*inv)%p;
		int j = hash_find(b);
		if(j != -1) return i * m + j;
	}
	return -1;
}
int p,a,b;
int ans;
int main()
{
	p = read(); a = read(); b = read();
	ans = BSGS(a,b,p);
	if(ans == -1) printf("no solution");
	else printf("%d",ans);
	return 0;
}

擴充套件

(a,p)̸=1(a,p)\not=1呢?
這時就要用到擴充套件BSGSBSGS了。

我們考慮將此情形變成(a,p)=1(a,p)=1的情況。

b=0b=0,即可立刻得出答案。

否則,對於模方程axb(mod&ThinSpace;&ThinSpace;p)a^x\equiv b(\mod p),設g=gcd(a,p)g=\gcd(a,p)
顯然,若gbg \nmid b,則此方程無解。
若方程有解,同時除以gg,得:
agax1bg(mod&ThinSpace;&ThinSpace;pg)\frac{a}{g}a^{x-1}\equiv \frac{b}{g}(\mod \frac{p}{g})
x=x1,p=pg,b=bg×(ag)1x&#x27;=x-1,p&#x27;=\frac{p}{g},b&#x27;=\frac{b}{g}\times (\frac{a}{g})^{-1},則方程變為:
axb(mod&ThinSpace;&ThinSpace;p)a^{x&#x27;}\equiv b&#x27; (\mod p&#x27;)
遞迴計算xx&#x27;,則答案為x=x+1x=x&#x27;+1