【BSGS 求a^x=b(mod n) 的最小解x】

例题 :POJ - 2417
求 a^x = b (mod n) 的最小非负解x 。 ( gcd(a,n)==1 )
分析: 本次分析 只针对 gcd(a,n)==1的情况 。

首先 我们再把问题转化一下 我们将n设置为素数,n不是素数一会讨论。
现在我们已知 gcd(a,n)==1, p为素数.
根据以上条件,我们可以利用费马小定理 a^(p-1) =1(mod p) 知道 这个式子的周期为p-1,(注意这里p-1只是其一个周期,不一定为最小正周期),知道其一个周期了,那么我们遍历[0,p) 第一个成立不就是最小的解? 理论可行,但是时间复杂度还是远远不行。 接下来我们来优化这个思路(其实还是暴力,不过是更加的优雅 QAQ ) 。
这里设 x= i*m-j ,其中m=ceil(sqrt(p)) . 0< i <=m . 0 <= j < m .
你肯定会问 为什么这么设?麻烦你现在利用上述条件将x的取值范围确定出来, 发现没有?x的范围 为[0,m*m] ,然后其覆盖了式子的那个周期p-1.
然后我们将x带入原式子得 a^(i*m) = b * a^j (mod p ) , 然后我们从小到大遍历j,将 b * a^j , j 插入hash中,为什么要从小到大遍历呢? 因为有的时候不同的j会产生相同的值,然后因为我们是从小到大遍历的,覆盖的时候最后的 j 一定是大的,这样的话,对于相同的i,j是更大的最后的 值 i * m - j一定是最小的 , 同样的我们再从小到大遍历i 就行了,第一个能够在hash中找到的一定是最小的值 x = i * m - j 。
现在我们在讨论一下n不是素数的情况,其实也很简单 ,只要gcd(a,n)==1条件成立 ,则一定有欧拉定理 a^phi(n)=1(mod p)成立,其最小解一定在phi(n)内,又因为 phi( n ) < n 所以我们只要遍历到n足够了,所以n是不是素数都足够解了 。

kuangbin的板子
模板条件 : gcd(a,n)==1 .

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
#define state  int

const int N = 100000+3;
const int MAXM = 1e6;
const int mod = 7653;
const int inf = 0x3f3f3f3f;

struct HASH{
    state  hs;
    int id,next;
}MAP[N+3];
int head[mod],top;
void init(){
    memset(head,-1,sizeof(head));
    top=1;
}
void Insert(state a,int b){
    int c=a%mod;
    MAP[top].hs=a; MAP[top].id=b; MAP[top].next=head[c];
    head[c]=top++;
}
int Find(state x){
    int c=x%mod;
    for(int i=head[c];i!=-1;i=MAP[i].next){
        HASH h=MAP[i];
        if(h.hs==x) return h.id;
    }
    return -1;
}

int BSGS(int a,int b,int n){ // n是不是素数都可以
    init();
    if(b==1) return 0;
    int m=sqrt(n*1.0);int j;
    long long  x=1,p=1;
    for(int i=0;i<m;++i,p=p*a%n) Insert(p*b%n,i);

    for(long long  i=m;;i+=m){
        if((j=Find(x=x*p%n))!=-1) return i-j;
        if(i>n) break;
    }
    return -1;
}

int main(){
    int  a,b,p;
    while(cin>>p>>a>>b){
        int ans=BSGS(a,b,p);
        if(ans==-1) puts("no solution");
        else cout<<ans<<endl;
    }
return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值