BSGS算法及拓展

Posted yanshannan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BSGS算法及拓展相关的知识,希望对你有一定的参考价值。

https://www.zybuluo.com/ysner/note/1299836

定义

一种用来求解高次同余方程的算法。
一般问题形式:求使得(y^xequiv z(mod p))的最小非负(x)

(BSGS)算法

要求(p)是质数。
由费马小定理可知,(y^{p-1}equiv1(mod p)),所以暴力枚举只要枚举到(p?1)即可。
但是由于(p)一般都很大,所以一般都跑不动。。。

优化算法(ing...)
现在令(x=mi?j)(其中(m=lceilsqrt p ceil))。
则方程可化为(y^{mi-j}equiv z(mod p)),
(y^{mi}equiv y^jz(mod p))

然后可以发现(j<m)(否则(x)就是负数)
所以我们可以暴力枚举(j),与所得(y^jz(mod p))的存在哈希表里,然后再暴力枚举(i),最后得出结果。

还要注意一些边界:

  • (y!=0)
  • (z=1)(puts("no solution"))
  • (i)的边界是([1,m+1])

一道Poj上的板子题
[SDOI2011]计算器

struct Hash_Table
{
  int h[N],cnt;
  struct Edge{int u,v,nxt;}e[N*10];
  il void clear(){memset(h,-1,sizeof(h));cnt=0;}
  il void add(re int u,re int v,re int w){e[++cnt]=(Edge){w,v,h[u]};h[u]=cnt;}
  il int Query(re int x)
  {
    re int t=x%mod;
    for(re int i=h[t];i+1;i=e[i].nxt)
      if(e[i].u==x) return e[i].v;
    return -1;
  }
  il void solve(re int y,re int z,re int p)
  {
    y%=p;z%=p;
    if(!y) {puts("no solution");return;}
    if(z==1) {puts("0");return;}
    re int m=sqrt(p)+1;clear();
    for(re int i=0,t=z;i<m;++i,t=1ll*t*y%p) add(t%mod,i,t);
    for(re int i=1,tt=ksm(y,m,p),t=tt;i<=m+1;++i,t=1ll*t*tt%p)
      {
    re int j=Query(t);if(j==-1) continue;
    printf("%d
",i*m-j);return;
      }
    puts("no solution");
  }
}BSGS;
int main()
{
  re int y,p,z;
  while(scanf("%d%d%d",&p,&y,&z)!=EOF)
    {
      BSGS.solve(y,z,p);
    }
  return 0;
}

拓展(BSGS)算法

不要求(p)是质数。
那就说明很可能(gcd(y,p)!=1),不满足费马小定理。
费马小定理提供了枚举上限,没有它这种问题就不好做了。。。

想想怎么把(y,p)约分。
(t=gcd(y,p))
把方程改写成等式形式:[y^x+kp=z]
分析一下,可以发现(z)一定是(t)的倍数。
(t):[frac{y}{t}y^{x-1}+frac{p}{t}k=frac{z}{t}]
接下来再次检查(gcd(y,frac{z}{t}))是否为(1),若否,说明还可以继续约分,理由同上。

最后形式为(那个(t)反正是个正整数)[frac{y^k}{t}y^{x-k}equivfrac{z}{t}(mod frac{p}{t})]

注意边界:

  • 如果(t>1)并且(z\%t>0),方程无解
  • 约分完的石子带到普通(BSGS)中时要带系数

咕谷模板题

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define il inline
#define re register
#define ll long long
#define fp(i,a,b) for(re int i=a;i<=b;++i)
#define fq(i,a,b) for(re int i=a;i>=b;--i)
using namespace std;
const int N=5e4,mod=45807;
il ll ksm(re ll S,re ll n,re int p)
{
  re ll T=S;S=1;
  while(n)
    {
      if(n&1) S=S*T%p;
      T=T*T%p;
      n>>=1;
    }
  return S;
}
struct Hash_Table
{
  int h[N],cnt;
  struct Edge{int u,v,nxt;}e[N*10];
  il void clear(){memset(h,-1,sizeof(h));cnt=0;}
  il void add(re int u,re int v,re int w){e[++cnt]=(Edge){w,v,h[u]};h[u]=cnt;}
  il int Query(re int x)
  {
    re int t=x%mod;
    for(re int i=h[t];i+1;i=e[i].nxt)
      if(e[i].u==x) return e[i].v;
    return -1;
  }
  il void solve(re int y,re int z,re int p)
  {
    if(z==1) {puts("0");return;}
    re int k=0,a=1;
    while(233)
      {
    re int t=__gcd(y,p);if(t==1) break;
    if(z%t) {puts("No Solution");return;}
    z/=t;p/=t;++k;a=1ll*a*y/t%p;
    if(z==a) {printf("%d
",k);return;}//有意思的地方
      }
    re int m=sqrt(p)+1;clear();
    for(re int i=0,t=z;i<m;++i,t=1ll*t*y%p) add(t%mod,i,t);
    for(re int i=1,tt=ksm(y,m,p),t=1ll*a*tt%p;i<=m+1;++i,t=1ll*t*tt%p)
      {
    re int j=Query(t);if(j==-1) continue;
    printf("%d
",i*m-j+k);return;
      }
    puts("No Solution");
  }
}BSGS;
int main()
{
  re int y,p,z;
  while(scanf("%d%d%d",&y,&p,&z))
    {
      if(!p&&!y&&!z) break;
      BSGS.solve(y,z,p);
    }
  return 0;
}















以上是关于BSGS算法及拓展的主要内容,如果未能解决你的问题,请参考以下文章

BSGS(Baby-Step-Giant-Step)算法及其应用

POJ 3243Clever Y 拓展BSGS

SPOJPower Modulo Inverted(拓展BSGS)

poj3243拓展BSGS(附hash版)

BSGS及扩展BSGS模板

bsgs算法