数论之高次同余方程(Baby Step Giant Step + 拓展BSGS)
什么叫高次同余方程?说白了就是解决这样一个问题:
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=1%C; LL g=0,d;
while( ( d=gcd(A,C) ) !=1 )
{
if(B%d)return -1;
B/=d;C/=d;
g++;D=D*(A/d)%C;
}
最后我们的方程就变为了k*a^(x-g) == b' (%c')
用BSGS解出x后加上g就是答案。