原根和离散对数BSGS求法(高次同余方程)

原根&离散对数


一.原根

1.定义:

(a与m互质)使\large a^{d}\equiv 1(mod m)成立的最小的d(记住原根是a,不是d!)

2.原根的性质:一般给出p(有时叫m)

1.具有原根的数字仅有以下几种形式:\large 2,4,\large p^{n},2p^{n}(p是奇质数)

2.一个数的最小原根的大小不超过\large m^{1/4}

3.原根个数Φ(Φ(m))个,m为质数则原根个数Φ(m-1)

3.求解原根的基本步骤:

  1. 判断一个数是否有原根。(通过性质1,枚举质数即可)
  2. 求得最小原根。(通过性质2,依次枚举2~\large m^{1/4}判断即可)
  3. 求出所有原根。(通过性质3,枚举次数d即可)

4.代码

5.应用 HDU5377  (准备另开一篇)

poj 1284 性质3质数的原根个数

原根可以把求指数问题转化为求对数问题,这样实现乘到加的转换,复杂度大大降低

2.离散对数


1.定义
普通对数:     \large a^{x}=b     记做\large x=log{_{b}}a
离散对数就是把这个放在了模意义下,即求解方程: a^x≡b(mod p)

2.求解方法(扩展版BGBS 大步小步算法)  参考代码

这个模板用的二分,复杂度多个logn,有些模板用map或hash,hash可以根号n解决

#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Inf (1<<29)
#define LL long long
using namespace std;
const int MM=110;
const long long MOD=1000000007;
///扩展大步小步算法
struct baby///小步算法预存的结构体定义
{
    long long b,j;
    bool operator < (const baby &other)const{
        if(b==other.b)return j<other.j;
        return b<other.b;
    }
}babyv[100005];
long long extended_euclid(long long a,long long b,long long &x,long long &y)
{
     long long d;
     if(b == 0) {x = 1;     y = 0;     return a;}
     d = extended_euclid(b, a % b, y, x);
     y -= a / b * x;
     return d;
}
long long Inv(long long a,long long n)
{
     long long d, x, y;
     d = extended_euclid(a, n, x, y);
     if(d == 1)	return (x%n + n) % n;
     else			return -1; // no solution
}
long long gcd(long long x,long long y)
{
    if(y==0)return x;
    return gcd(y,x%y);
}
///快速幂,x^y%mod
long long q_pow(long long x,long long y,long long mod)
{
    long long ans=1;
    while(y>0)
    {
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
///小步预存的个数为m的结构体里面查找num
long long find(long long num,long long m)
{
    long long l=0,r=m-1,ans=-1;
    while(r>=l)
    {
        long long mid=(r+l)/2;
        if(babyv[mid].b>=num){
            if(babyv[mid].b==num)
                ans=babyv[mid].j;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
///A^x=B(modC)求解x,gcd(A,C)不一定等于1,无解返回-1
long long ex_babystep_giantstep(long long A,long long B,long long C)
{
    for(long long i=0;i<=100;i++)///先在小于100的数里面测试
        if(q_pow(A,i,C)==B%C)return i;
    ///消因子, 将A^x=B%C化为d*A^(x – n)=B%C
    long long g,n=0,d=1;
    while((g=gcd(A,C))!=1)
    {
        if(B%g)return -1;///无解
        n++;
        B/=g;
        C/=g;
        d=(d*A/g)%C;
    }
    ///无扩展小步大步操作
    long long m=ceil(sqrt(C)),ans=1;
    for(long long j=0;j<m;j++)///预存操作
    {
        babyv[j].b=ans;
        babyv[j].j=j;
        ans=(ans*A)%C;///递推
    }
    ans=1;
    sort(babyv,babyv+m);///预存排序
    long long Bm=q_pow(Inv(A,C),m,C);///算出A^(-m)%C的值
    for(long long i=0;i<m;i++)
    {
        long long j=find(Inv(d,C)*B%C*ans%C,m);///二分查找
        if(j!=-1)return i*m+j+n;///找到返回答案
        ans=(ans*Bm)%C;///继续递推
    }
    return -1;///找不到答案
}
int main()
{
    long long X,Z,K;
    //freopen("D:\\o.txt","r",stdin);
    while(~scanf("%lld%lld%lld",&X,&Z,&K))
    {
        if(X==0&&Z==0&&K==0)break;
        long long ans=ex_babystep_giantstep(X,K,Z);//x y C
       // if(ans==-1)puts("No Solution");
      //  else printf("%lld\n",ans);
    }
    return 0;
}


 

猜你喜欢

转载自blog.csdn.net/zjyang12345/article/details/89061809