数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)

时间:2021-02-22 06:31:46

什么叫高次同余方程?说白了就是解决这样一个问题:

A^x=B(mod C),求最小的x值。


baby step giant step算法

题目条件:C是素数(事实上,A与C互质就可以。为什么?在BSGS算法中是要求a^m在%c条件下的逆元的,如果a、c不互质根本就没有逆元。)

如果x有解,那么0<=x<C,为什么?

我们可以回忆一下欧拉定理:

对于c是素数的情况,φ(c)=c-1

那么既然我们知道a^0=1,a^φ(c)=1(在%c的条件下)。那么0~φ(c)必定是一个循环节(不一定是最小的)。既然是%c,那么B一定是0到c-1之间的一个数。最坏的条件下,a的φ(c)以内次方%c的余数各不相同,0<=x<C时一定存在一个x满足条件。根据抽屉原理,如果在大于c的x中出现一个x值满足条件,那在它以前一定有一个更小的x值满足条件。

BSGS的算法是这样的:

首先取m=sqrt(c)向上取整。(为什么取sqrt(c)?我也不是很懂,是为了算法的效率平衡。)

然后先预处理a的0到m次方。

a^x=b ( %c )
设x=i*m+j;
即: i为x/m,j为x%m。
a^(i*m+j)=b;
b * (a^(-m))^i = a^j ( %c )

先枚举j,把右边存起来(Hash 或者 普通数组,下一步用二分查找)
枚举i,如果左边的数值曾经存储过(b * (a^(-m))^i = a^j),则 x=i*m+j。

求a^(-m):(就是a^m的逆元)

有两种方法:

方法一:根据欧拉定理

设A=a^m,那么A^φ(c)==1(%c)

A^(φ(c)-1)*A==1(%c)

到这里已经可以得到A的逆元为A^(φ(c)-1)。

继续推下去,根据c是素数,φ(c)=c-1

那么A的逆元就是A^(c-2)

方法二:相当于解a^m*x-C*y=1,根据拓展欧几里得出x就是逆元。

BSGS主要就是要注意细节,注意要去重(余数相同时只要取较小的一个)。


拓展BSGS

如果a跟c不互质,那该怎么办?

其实只需要加一小段代码就可以。

首先,我们知道:

A%C=B,那么就是A-C*x=B,如果d=gcd(A,C),且B%d==0,那么(A/d)%(C/d)=B/d是成立的。

那么我们就在A与C仍有不为一的公因数的时候,不断地从a^x中拿出一个a与c约分。过程中如果b%d!=0,那么在x>T的时候无解。

LL D=%C; LL g=,d;
while( ( d=gcd(A,C) ) != )
{
if(B%d)return -;
B/=d;C/=d;
g++;D=D*(A/d)%C;
}

最后我们的方程就变为了k*a^(x-g) == b' (%c')

用BSGS解出x后加上g就是答案。