版权声明:此文为作者原创,若觉得写得好请点个赞再离开。当然,也欢迎在讨论区指出本文的不足,作者会及时加以改正。转载请注明地址: https://blog.csdn.net/chenxiaoran666/article/details/83450304
前言
算法,全称
,即大小步算法。某些奆佬也称其为拔(Ba)山(Shan)盖(Gai)世(Shi)算法。
它的主要作用是求解形式如 的式子中 的值。
而且,它是一个简单易懂的算法(毕竟连我这样的数学渣渣都能理解)。
一个简单的性质
首先,我们需要知道一个简单的性质。
由费马小定理可得, 。
费马小定理详见博客筛素数方法(二)—— 费马小定理及MR素数判断
因此,当 时,会出现一个循环节。
于是我们就能保证答案 如果存在,则必然 。
这是一个简单而又重要的性质。
算法的主要思想
算法的主要思想就是两个字:分块(提到分块就要 一波分块奆佬 )。
根据分块思想,我们设一个变量 (注意,此处要用 函数向上取整,这样才能保证 ,不然可能会遗漏答案)。
不难发现,此时的 可以表示为 ( 均为非负整数且 )。
那么原式就被转化成了 。
移项得 。
然后怎么处理呢?
我们可以对 的值进行一波预处理,用一个 存储下来。
然后枚举 ,判断 的值是否存在即可。
当找到一个合法的 后,最终的答案就是 。
时间复杂度分析
预处理的时间复杂度显然是 的,枚举 的时间复杂度显然是 的。
又由于 和 都是 大小的,所以总复杂度也是 级别的,是一个比较优秀的算法。
代码
map<int,int> s;//定义一个map
inline int BSGS(int x,int y,int MOD)//对于一个式子x^t=y(mod MOD),求出t的值
{
register int i,t=1,base,Size=ceil(sqrt(MOD));//注意此处要用ceil函数向上取整
for(i=0;i<=Size;++i) s[1LL*t*y%MOD]=i,base=t,t=1LL*t*x%MOD;//预处理将(x^j)*y的值全部用map存下对应的j,并用base存储下x^Size
for(t=base,i=1;i<=Size;++i,t=1LL*t*base%MOD)//枚举i,每次将t乘上x^Size
if(s[t]) return i*Size-s[t];//找到一个合法的i,则答案就是i*Size-j
return 0;//无解返回0
}