数论——阶与原根

# 整数的阶

根据欧拉定理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)?'\n':' ');
93         }
94     }
95 }

 

posted @ 2020-04-07 21:28  Hyx'  阅读(1558)  评论(0编辑  收藏  举报