设 m 是正整数,a是整数,若a模m的阶等于φ(m),则称 a 为 模m的一个原根。(其中φ(m)表示m的欧拉函数)
什么是a模m的阶?
设m>1,gcd(a,m)=1,使得
a
r
≡
1
(
m
o
d
m
)
a^{r} \equiv 1 \pmod {m}
ar≡1(modm)成立的最小的r,称为a对模m的阶。
性质:
如果一个数有原根,那么它一共有
φ
(
φ
(
m
)
)
\varphi(\varphi(m))
φ(φ(m))个原根。
如果p为素数,那么素数p一定存在原根,并且模p的原根的个数为
φ
(
p
−
1
)
\varphi(p-1)
φ(p−1)个。
求解方法
对
φ
(
m
)
\varphi(m)
φ(m)素因子分解,若m为素数p, 对于素数来说就是分解
p
−
1
p-1
p−1的质因子,即P-1=(P1 ^ a1) (P2 ^ a2)……(Pk ^ ak)。
枚举a ~ [2, p-1],若对于所有
p
i
p_i
pi恒有
a
p
−
1
p
i
!
=
1
m
o
d
  
m
a^{\frac{p-1}{p_i}} !=1 \mod{m}
apip−1!=1modm,那么这个a就是p的原根。
例题:51nod1135(题意:求素数p的最小原根)
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#pragma GCC diagnostic error "-std=c++11"
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e6 + 10;
bitset<N> vis; //判断素数
vector<ll> pri, ans; //pri储存素数,an储存质因子
ll p; //求p最小原根
void is_prime() //线性筛求质数
{
vis.set(); //全置为1
for(int i = 2; i < N; i++){
if(vis[i]){
pri.push_back(i);
for (int j = i + i; j < N; j+=i)
vis[j] = 0;
}
}
}
void divide(ll n) //分解质因子
{
ans.clear();
for (auto x : pri){
if(x * x > n)
break;
if(n % x == 0){
ans.push_back(x);
while(n % x == 0) //相同质因子只取一次
n /= x;
}
}
if(n > 1)
ans.push_back(n);
}
ll pow(ll x, ll n, ll mod) //快速幂取模
{
ll ans = 1;
for (; n; n>>=1, x=x*x%mod){
if(n&1)
ans = ans * x % mod;
}
return ans;
}
int solve(ll a)
{
for(auto x : ans){
if(pow(a, (p-1)/x, p) == 1)
return 0;
}
return 1;
}
int main()
{
is_prime(); //打素数表
scanf("%lld", &p);
divide(p - 1); //分解phi(p),对于素数p,就是分解p-1
for (ll i = 2; i <= p - 1; i++){
if(solve(i)){
printf("%lld\n", i);
break;
}
}
return 0;
}