离散对数和原根

来源:互联网 发布:离线基站数据库 300万 编辑:程序博客网 时间:2024/05/16 07:24

原根与阶

阶定义

(a,m)=1, 满足 ax1(mod m) 的最小的 x,称为a对m的阶,记为
ordm(a)
ordm(a)=ϕ(m) 时称为a为m的原根.

简单性质

  1. ax1ordm(a)x
  2. ordm(a)ϕ(m)
  3. a0,a1,,aordm(a)1 构成摸m的既约剩余系.
  4. axay(mod m)xy(mod ordm(a))
  5. ordm(ad)=ordm(a)(ordm(a),d)(利用这个性质可以求出所有原根)
  6. m(若存在原根)的原根数目为 ϕ(ϕ(m)) .
  7. nn=2,4,pe,2pe(p).
  8. ϕ(m)=pr11pr22prkk,则gm的原根当且仅当对与所有的pi.
    gϕ(m)pi1(mod m)

求阶和原根的方法

上面的性质是非常容易证明的.随便找一本数论书籍都会有详细的证明.由上面的性质我们可以得到一个相对简单的求阶和原根的方法(暴力)

  • 阶: 我门可以对m先分解因子,设m=pr11pr22prkk ,然后将ri逐个相减记为p直到再减一个之后ap1
  • 原根: 这个就更加暴力了,我们可以用性质8逐一枚举与m互素的数然后对分解式进行验证就好.

这里有一份51nod模板题1135的代码

#include <cstdio>#include <iostream>#include <vector>#include <queue>#include <algorithm>#include<cmath>#include <cstring>#include <map>#include <iomanip>#define fi first#define se second#define INF 0x3f3f3f3fusing namespace std;const int MOD = 1e9+7;const int MAX_P = 2e4+10;const int maxn =2e5+10;const int MAX_V = 5e5+10;const int maxv = 1e6+10;typedef long long LL;typedef long double DB;typedef pair<int,int> Pair;int p;int prime[maxn],cnt;int factor[maxn],fact_cnt;void init_prime(){    memset(prime,0,sizeof(prime));    cnt = 0;    for(int i = 2 ; i<maxn ; ++i){        if(!prime[i]){            prime[cnt++] = i;        }        for(int j =0 ; j<cnt && prime[j]*i<maxn ; ++j){            prime[prime[j]*i] = 1;            if(i % prime[j] == 0)break;        }    }}LL power_mod(LL x,LL n,LL mod){    LL res =1;    while (n) {        if(n & 1)res = res*x % mod;        x = x*x % mod;        n >>= 1;    }    return res;}void get_fact(int n){    fact_cnt = 0;    for(int i = 0 ; i< cnt && prime[i]*prime[i] <=n ; ++i ){        if(n % prime[i] == 0){            factor[fact_cnt++] = prime[i];            while (n%prime[i] == 0)n/=prime[i];        }    }    if(n!=1)factor[fact_cnt++] = n;}bool check(int g){    for(int i=0 ; i<fact_cnt ; ++i)        if(power_mod(g,(p-1)/factor[i],p) ==1)return false;    return true;}int proot(int p){    get_fact(p-1);    for(int i=2 ; i<p ; ++i)        if(check(i))return i;}int main(int argc, char const *argv[]) {    init_prime();    cin>>p;    std::cout << proot(p) << '\n';  return 0;}

离散对数

bsgs algorithm

AxC(mod m)
大步小步算法,这个算法有一定的局限性,只有当gcd(a,m)=1.

原理

简单说一下它的原理.其实由上面的性质,我们知道0x<ordm(a),设x=uTv则原方程转化为auTCav(a1) 我们可以预处理出每一个 Cav对每一个计算出的 auT 找一下有没有等于 Cav 的取(T=m)复杂度 O(mlg(m))

扩展大步小步

上面的过程能求离散对数的前提是 a1 存在即若 gcd(a,m)=1 则大步小步是可用的,现在我们来扩展一下大步小步算法.使得即使对任意的 m 都能求出x或者不存在时返回-1.
由于模方程axb(mod m)adxbd(mod md),d=gcd(a,m)(证明见《初等数论及其应用》),我们可以对a,m,b 不断消因子,直到 (a,m)=1 设共进行 δ 次消因子操作,那么 δ 次操作以后就变成了 axδaδb(mod m),(若消因子中途遇见b不能整除返回-1,若中途遇见baδ , 直接返回 δ).现在就可以用大步小步算法来求解了.详细内容请看代码.

模板题spoj MOD - Power Modulo Inverted

#include <cstdio>#include <iostream>#include <vector>#include <queue>#include <algorithm>#include<cmath>#include <cstring>#include <map>#define fi first#define se second#define INF 0x3f3f3f3fusing namespace std;const int MOD = 1e9+7;const int MAX_P = 2e4+10;const int maxn =500+10;const int MAX_V = 5e5+10;const int maxv = 1e6+10;typedef long long LL;typedef pair<int,int> Pair;LL power_mod(LL x,LL n, int mod){   LL res =1;   while (n) {      if(n&1)res = res*x % mod;      x = x*x %mod;      n >>=1;   }   return res;}LL bsgs(LL A,LL C,LL mod){   A %= mod;C %= mod;   if(C==1)return 0;   LL cnt =0;   LL tmp = 1;   for(LL g = __gcd(A,mod) ; g != 1 ; g = __gcd(A,mod)){      if(C % g)return -1;//不能整除      C /=g ; mod/=g ; tmp = tmp*A/g%mod;      ++cnt;      if(C == tmp)return cnt;   }   //大步小步a^xa^cnt=C (mod m)a^cnt = tmp;   LL T = (LL)sqrt(0.5+mod);   LL b = C;   map<LL,LL> hash;   hash[b] = 0;   for(int i=1 ; i<=T ; ++i){      b = b*A%mod;//当mod为LL时注意溢出      hash[b] = i;   }   A = power_mod(A,T,mod);   for(int u =1 ; u<=T ; ++u){      tmp = tmp*A %mod;      if(hash.count(tmp))return u*T-hash[tmp]+cnt;   }   return -1;}int main(int argc, char const *argv[]) {   LL x,y,z,k;   while (scanf("%lld%lld%lld",&x,&z,&k ) && z) {      y = bsgs(x,k,z);      if(y==-1)std::cout << "No Solution" << '\n';      else std::cout << y << '\n';   }   return 0;}
0 0
原创粉丝点击