• 数论——阶与原根


    # 整数的阶

    根据欧拉定理aφ(n≡ (mod n),其中a与n互质,则至少存在一个x使得a≡ (mod n)

    阶的定义:

    • a和n互质,使得 a≡ (mod n) 成立的最小的正整数x就是a模n的阶,记做ordna,若ordna=x,ordnat=x/gcd(x,t)

    性质:

    • 若a与n是互质的整数,且n > 0,那么正整数x是同余式a≡ (mod n)解的充要条件是ordna | x,易知ordna | φ(n)

    # 原根

    定义:

    • 若 a 与 n 是互质的整数且n>0,0 < a < n,那么当 ordna = φ(n),称a为模n的原根。

    性质:

    • 每个素数都有原根,只是整数不一定有原根
    • 当正整数n有原根时,有φ(φ(n))个原根
    • 模n有原根的充要条件是n=2,4,pk,2pk,其中p是奇素数,k为任意正整数
    • 若a为模n的原根,则ad为模n的原根的充要条件是gcd(d,φ(n))=1
    • a0,a1,a2,......,aordna -1 模n 两不同余,所以当a是模n的原根时,           a0,a1,a2,......,aordna -1构成模n的简化剩余系列

    # 求原根

    求模 n 原根的方法:对φ(n)分解质因数,即φ(n)=p1c1 · p2c2 · ....... · pncn

    若恒有

    aφ(n)/pi ≠ 1(mod n) i ∈ [1,n]

    暴力从2到n枚举即可,原根的大小很小,一般不会超过300

    对φ(n)的所有质因子验证上式,如果都满足就是原根。

    # 模版

     1 int primes[N+10],cnt;
     2 ll euler[N];
     3 bool st[N+10];
     4 vector<ll>ans;
     5 vector<int> factors;//质因子集合,只有质数,没有指数
     6 void get_primes(){
     7     st[1] = 1;
     8     euler[1] = 1;
     9     for (int i = 2; i <= N; i ++ ) {
    10         if (!st[i]) {
    11             primes[cnt++] = i;
    12             euler[i] = i - 1;
    13         }
    14         for (int j = 0; primes[j] <= N / i; j++) {
    15             int t = primes[j] * i;
    16             st[t] = true;
    17             if (i % primes[j] == 0) {
    18                 euler[t] = euler[i] * primes[j];
    19                 break;
    20             }
    21             euler[t] = euler[i] * (primes[j] - 1);
    22         }
    23     }
    24 }
    25 int gcd(int a,int b){return b?gcd(b,a%b):a;}
    26 int qmi(ll a,ll k,ll p){
    27     ll res=1;
    28     while(k){
    29         if(k&1) res=res*a%p;
    30         a=a*a%p;
    31         k>>=1;
    32     }
    33     return res;
    34 }
    35 void get_factors(int n)
    36 {
    37     factors.clear();
    38     for (int i = 2; i * i <= n; ++i)
    39     {
    40         if (n % i) continue;
    41         factors.push_back(i);
    42         do n /= i;
    43         while (n % i == 0);
    44     }
    45     if (n > 1) factors.push_back(n);
    46 }
    47 //判断原根是否存在
    48 bool judge(int n){
    49     // 模n有原根的充要条件是n=2,4,p^n,2p^n,其中p是奇素数,n为任意正整数
    50     if(n%2==0) n/=2;
    51     if(!st[n]) return true;//奇素数,有原根
    52     for(int i=3;i*i<=n;i++){//偶素数只有2,从3开始即可
    53         if(n%i==0){//遇到质因数除尽
    54             while(n%i==0) n/=i;
    55             return n==1;//能是有奇素数构成
    56         }
    57     }
    58     return false;
    59 }
    60 int main(){
    61     long long n;
    62     cin>>n;
    63     ans.clear();
    64     if(n==2){ puts("1");continue;}
    65     if(n==4){puts("3");continue;}
    66     if(!judge(n)) {puts("-1");continue;}//不存在原根
    67     int phi=euler[n];
    68     int aa=-1;//存最小的原根
    69     get_factors(phi);
    70     for(int a=2;a<n;a++){//求出一个最小的原根
    71         bool flag=true;
    72         aa=a;
    73         if(qmi(a,phi,n)!=1) continue;//原根定义进行判断剪枝
    74         for(int i=0;i<factors.size();i++){//枚举phi(n)的质因子
    75             if(qmi(a,phi/factors[i],n)==1){//即小于phi(n)/p[i]也满足等式,不是原根
    76                 flag=false;
    77                 break;
    78             }
    79         }
    80         if(flag){
    81             ans.pb(a);break;//a就是最小的原根
    82         }
    83     }
    84     if(ans.size()==0) puts("-1");
    85     else {
    86         for(int i=2;i<=phi;i++){//求所有的原根
    87             if(gcd(i,phi)==1) ans.pb(qmi(aa,i,n));
    88         }
    89         sort(ans.begin(),ans.end());
    90         unique(ans.begin(),ans.end());
    91         for(int i=0;i<ans.size();i++){
    92             printf("%lld%c",ans[i],(i==ans.size()-1)?'
    ':' ');
    93         }
    94     }
    95 }
  • 相关阅读:
    225. 用队列实现栈
    415. 字符串相加
    rabbitmq的基本使用
    3. 无重复字符的最长子串
    面试题59
    面试题30. 包含min函数的栈
    面试题09. 用两个栈实现队列
    287. 寻找重复数
    1137. 第 N 个泰波那契数
    70. 爬楼梯
  • 原文地址:https://www.cnblogs.com/hhyx/p/12656217.html
Copyright © 2020-2023  润新知