\(BSGS\)
\(BSGS(Baby Step Giant Step)\),中文名大步小步算法,也有拔山盖世、北上广深、阿姆斯特朗算法等别称
解决问题为求\(A^x\equiv B(mod C)\)的最小整数解,其中\(A C\)互质。
首先因为\(\phi(C)\)一个循环节且\(\phi(C)<C\),所以答案不会超过\(C\)
使得\(x=i\times m-j\),其中\(m=\sqrt C\)
这样原式变为\(A^{im-j}\equiv B(mod C)\)
移项得到\(A^{im}\equiv BA^j(mod C)\)
大小步中的"小步"就是从\(0\)到\(m-1\)枚举\(j\)(相当于个位),将\((BA^j,j)(mod C)\)记录进哈希表
然后“大步”就是枚举\(i\),相当于枚举\(m\)进制的第\(2\)位然后对于每一个\(A^{im}(mod C)\)进入哈希表查找是否存在,若存在一定是最小值直接返回即可。
因为c++整除的性质,一定要确保枚举的i超过C,否则会遗漏答案
注意答案只会在\(C\)以下,如果超过\(C\)还没找到就可以返回\(-1\)了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
#include<cmath>
#define ll long long
#define re register
#define MOD 76543
using namespace std;
int T,aa,bb,cc,K;
int top,hs[MOD],id[MOD],head[MOD],next[MOD];
ll x,y;
map<int,int> a;
void insert(int x, int y)
{
int k = x%MOD;
hs[top] = x, id[top] = y, next[top] = head[k], head[k] = top++;
}
int find(int x)
{
int k = x%MOD;
for(int i = head[k]; i != -1; i = next[i])
if(hs[i] == x)
return id[i];
return -1;
}
ll BSGS(ll a,ll b,ll c)
{
a%=c,b%=c;//一定别忘了取模 1
if(a==0&&b==0) return 1;//最小整数解 2
if(b == 1)
return 0; // 4
if(gcd(a,c)!=1) return -1;// 3 因为题目保证是质数,不互质的话一定是倍数关系
//取模之后的gcd答案一定为C,此时C是质数,即使扩展BSGS也无解
memset(head, -1, sizeof(head));//数组不清空,爆零两行泪
top = 1;
int m = sqrt(c*1.0), j;
long long x = 1, p = 1;
for(int i = 0; i < m; ++i, p = p*a%c)
insert(p*b%c, i);//存的是(a^j*b, j)
for(long long i = m; ;i += m)//此时p为a^m
{
if( (j = find(x = x*p%c)) != -1 )
return i-j; //a^(ms-j)=b(mod c)
if(i > c)
break;
}
return -1;
}
1.当\(a\)在模\(b\)意义下有逆元的充要条件是\(gcd(a,b)=1\)
上面做法虽然用\(a^{im-j}\)而不是\(a^{im+j}\)躲过了逆元操作
但实际上这个操作的逆运算如果不保证\(A,C\)互质时不成立的,所以\(BSGS\)的前提是\(A,C\)互质
2.还有一定要注意C是否为质数,如果为质数且\(A,C\)不互质,则\(A\)为倍数,这时候考虑B是否为0,否则\(BSGS\)和扩展\(BSGS\)都无解
因为扩展的要让\(C\)除一个数,无法整除则无解。
3.注意特判的顺序,先取模,再考虑\(a==0,b==1\)或\(a==0,b==0\)的情况下,最后再判无解
\(Ext. BSGS\)
当\(A,C\)不互质时
1.首先对于一般的离散对数问题\(A^x\equiv B(mod C)\),一定可以写成\(A^x=kC+B\)
2.令\(d=gcd(A,C)\),将两边同时除以\(d\),得到\(\frac{A}{d}A^{x-1}=kC_2+B_2\)
3.注意\(A\)是没有变的,所以\(A,C\)任然可以互质,重复以上操作直到\(A,C\)互质,令\(D=\prod\limits_{i=1}^n\),转换为\(D\cdot A^{x-n}=kC_n+B_n\)即\(D\cdot A^{x-n}\equiv B_n(mod C_n)\),变为互质情况,\(BSGS\)即可
4.由于这里的指数是\(x-n\),需要特判断\(x=0,1,\cdots n\)时等式是否成立。
一般只用特判\(50\)层就够了,因为是\(log(C)\)的
警告!警告!使用前最好验证一下,因为扩展的BSGS我没写过题!
警告!警告!使用前最好验证一下,因为扩展的BSGS我没写过题!
警告!警告!使用前最好验证一下,因为扩展的BSGS我没写过题!
#define mod 76543
int hs[mod],head[mod],next[mod],id[mod],top;
void insert(int x,int y)
{
int k=x%mod;
hs[top]=x,id[top]=y,next[top]=head[k],head[k]=top++;
}
int find(int x)
{
int k=x%mod;
for(int i=head[k];i!=-1;i=next[i])
if(hs[i]==x)
return id[i];
return -1;
}
int BSGS(int a,int b,int c)
{
memset(head,-1,sizeof(head));//数组不清空,爆零两行泪
top=-1;
if(a==0&&b==0) return 1;
if(b==1)
return 0;
long long d=1;
int cnt=0;
long long r=1;
for(int i=0;i<50;++i)
{
if((r-b)%c==0)
return i;
r*=a;
r%=c;
}//最多50层就够了
while((d=gcd(a,c))!=-1)
{
if(b%d) return -1;//如果不能整除说明无解,因为此时没法用逆元
cnt++;
b/=d,c/=d;//注意让b先取模
}
int m=sqrt(c),j;
for(int j=0;j<m;++j,p=p*a%c)
insert(p*b%c,j);//注意取模,存的是(a^j*b,j);
for(int i=m;;i+=m)//因为是减法,所以从m开始枚举
{
if((j=find(x=x*p%c))!=-1)//p从一开始就是a^m每次乘p相当于a^i
return i-j;//im-j
if(i>c)//里面找不到循环节外不可能找到了
break;
}
return -1;
}