原根


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)/p ≡ 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;
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM