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;
}
}
}