1、原根的定義:
原根,是一個數學符號。設m是正整數,a是整數,若a模m的階等於φ(m)(m的歐拉函數),則稱a為模m的一個原根。
階:a和模m互質,使ad ≡1(mod m)成立的最小正整數d稱為a對模m的階。例如:22≡1(mod3),2對模3的階為2。
假設一個數g對於P來說是原根,那么gi mod p的結果兩兩不同,且有 1 < g < P , 0 < i < P.那么g可以稱為是P的一個原根。
歸根到底就是 gP-1 ≡ 1 (mod P)當且僅當質數為P-1的時候成立(P為素數).
求原根目前的做法只能是從2開始枚舉,然后暴力判斷g^(P-1) = 1 (mod P)是否當且當指數為P-1的時候成立,而由於原根一般都不大,所以可以暴力得到。
2、原根的性質:
(1)可以證明,如果正整數(a,m) = 1 和正整數d滿足ad ≡ 1(mod 7),則d整除φ(m)。因此Ordm(a)整除φ(m)。在例子中,
當a = 3 時,我們僅需要驗證3的1、2、3和6次方模7的余數即可。
(2)記 δ = Ordm(a),則a1 ,.... aδ-1 構成模m的簡化剩余系數。
(3)模m有原根的充要條件是m = 1,2 , 4 , p , 2p , pn , 其中p是奇質數,n是任意正整數。
(4)對正整數(a,m)= 1 , 如果a是模m的原根,那么a是整數n乘法群(即加法群 Z/mZ的可逆元,也就是所有與 m 互素的正整數構成的等價類構成的乘法群)Zn的一個生成元。由於Zn有φ(m)個元素,而它的生成元的個數就是它的可逆元個數,即φ(φ(m))個,因此當模m有原根時,它有φ(φ(m))個原根
3、求質數最小原根
求質數最小原根:對於質數p,φ(p) = p - 1 ,根據費馬小定理,ap-1≡1(mod p) 成立a為大於1的整數。所有只需驗證沒有比p-1小的數k
使得ak≡1(mod p).
而k不需要全部枚舉,只需枚舉p-1的質因數即可。(如果x為p-1的質因子,且ax ≡ 1(mod p),那么x的倍數nx顯然也滿足anx ≡ 1(mod p) ,所有p-1不為a對模p的階(不是最小),即不是原根)。
素數p(3<=p<=1e9),一般對於要多次分解質因數時,可以先篩選出素數,然后再素數里進行質因數分解。
因為這題只需進行一次,可以不需要篩素數,直接分解O(√n)。
首先求質冪分解Φ(m) = p1e1 * p2e2 * ...*pkek
然后枚舉g,若恆不滿足gΦ(m)/pi ≡ 1 mod(p) 其中i = 1,2....k.
則g是m的原根。
const int maxn = 1e5+9; const int N = 1e6+9; int vis[N], prime[maxn] , len , pf[maxn] , len1; void Erasieve(){ rep(i , 2 , N){ if(!vis[i]){ prime[++len] = i ; for(int j = i * i ; j <= N ; j += i){ vis[j] = 1 ; } } } } void oulasieve(){ rep(i , 2 , N){ if(!vis[i]){ prime[++len] = i ; } for(int j = 1 ; j <= len && prime[j] * i <= N ; j++){ vis[prime[j]*i] = 1; if(i % prime[j] == 0) break; } } } void divide(int x){ for(int i = 1 ; i <= len && prime[i] * prime[i] <= x; i++){ if(x%prime[i]==0){ pf[++len1] = prime[i]; while(x % prime[i] == 0){ x /= prime[i] ; } } } if(x > 1) pf[++len1] = x ; } /*void divide2(int x){ for(int i = 2 ; i * i <= x ; i++){ if(x % i == 0){ phi[++len1] = i ; while(x % i == 0) x /= i ; } } }*/ int quickpow(int a , int b , int p){ int ans = 1 ; while(b){ if(b&1){ ans = ans * a % p ; } b >>= 1 ; a = a * a % p ; } return ans ; } void solve(){ oulasieve(); int p ; cin >> p ; divide(p-1); rep(i , 2 , p-1){ int flag = 1 ; rep(j , 1 , len1){ if(quickpow(i , (p-1)/pf[j] , p) == 1){ flag = 0 ; break; } } if(flag == 1){ cout << i << endl; break; } } }