BSGS算法介绍
BSGS是用来解决形如 yx ≡ z mod p 的问题的,它能够在有解的情况下求出来最小值。这里前提是gcd(y, p) == 1,否则就需要exBSGS解决。
算法过程
首先我们让 x = i*m-k
这样上面的式子就变为了 yi*m/yk ≡ z mod p, 通过变换得到 yi*m ≡ z * yk mod p。其中m等于ceil(sqrt( p)),也就是向上取整根号p,k的范围是0~m-1。
通过枚举得到所有yk k∈ [0, m),并使用hash表或者map存储当前k值,然后在枚举左边的i,如果在hash表或者map里面能够找到,就代表找到了最小的解。输出i * m-map[num]就行了。
1,首先说为什么m等于ceil(sqrt§)呢?
没有为啥,m等于什么都行,但是当他等于ceil(sqrt§)时是最快的。
2,i的取值范围为什么是[1, m]?
首先需要保证x为正数,所以i一定大于0。然后通过欧拉定理的应用我们知道 at mod φ( p) = at mod p, 这样就保证了x一定是小于p的,因为p为素数的时候欧拉函数值最大为p-1。所以i * m-k <= p,而k的取值范围为k∈ [0, m),当k = 0的时候,i * m <= p, 又因为我们知道m取值为sqrt( p ), 所以i的取值范围是[1, m]。
3,为什么得到的一定是最小解?
这个算法叫做大小步算法,在记录所有yk k∈[0, m)的时候就是小步走,小步走时,如果遇到取模后相同的值就会用当前的k值替换到hash表里面的,这样最后找到的一定是最大的k,因此 i * m - k一定是最小值也就是最小解。
代码实现
// y^x = z mod p
void slove(ll y, ll z, ll p)
{
map<ll, int> s;
//特判一下,如果y是p的倍数一定无解
if (y%p == 0)
{
printf("Orz, I cannot find x!\n");
return;
}
y%=p, z%=p;
//特判
if (z == 1)
{
printf("0\n");
}
else
{ //求出来m的值,加一效果同ceil一样
int m = sqrt(p)+1;
ll t = z;
//求解y^k,并记录下来,k是从[0, m)的,
//这里为了让最后一个t值也记录下来,就循环到了m
for (int i=0; i<=m; i++)
{
s[t] = i+1; //记录位置加一,一会在加上来,因为判断需要用到值,没办法从0开始
t = t*y%p;
}
ll q = qul(y, m, p);
t = 1;
bool flag = false;
for (int i=1; i<=m; i++)
{
t = t*q%p;
if (s[t])
{
printf("%d\n", i*m-s[t]+1);
flag = true;
break;
}
}
if (!flag)
printf("Orz, I cannot find x!\n");
}
}