阶
定义:设 m > 1 , ( a , m ) = 1 m>1,(a,m)=1 m>1,(a,m)=1,使得 a l ≡ 1 ( m o d m ) a^l \equiv 1(mod~~m) al≡1(mod m)成立的最小 l l l,称为 a a a对模 p p p的阶,记做 δ m ( a ) \delta_m(a) δm(a)。
定理:若 a n ≡ 1 ( m o d m ) , m > 1 , ( a , m ) = 1 a^n \equiv 1(mod~~m),m>1,(a,m)=1 an≡1(mod m),m>1,(a,m)=1,则 δ m ( a ) ∣ n \delta_m(a)|n δm(a)∣n。
原根
定义:设 a , m a,m a,m均为正整数,若 a a a模 m m m的阶等于 φ ( m ) \varphi(m) φ(m),则称 a a a为模 m m m的一个原根。
推论:
- 设一个数 g g g对于模素数 p p p来说是原根,那么 g i ( m o d p ) , 1 < g < p , 0 ≤ i < p g^i(mod~~p),1<g<p,0 \leq i<p gi(mod p),1<g<p,0≤i<p的结果两两不同。
- 若模 m m m有原根,那么一共有 φ ( φ ( m ) ) \varphi(\varphi(m)) φ(φ(m))个。
模 m m m有原根的充要条件为 m = 2 , 4 , p k , 2 ∗ p k ( p ∈ p r i m e s , p > 2 ) m=2,4,p^k,2*p^k(p \in primes,p>2) m=2,4,pk,2∗pk(p∈primes,p>2)
求 m m m的原根
m m m为素数
若 m m m为素数 p p p,其欧拉函数值为 φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p−1,对 p − 1 p-1 p−1质因数分解,即 p − 1 = p 1 a 1 p 2 a 2 . . . p a k k p-1=p_1^{a_1}p_2^{a_2}...p^k_{a_k} p−1=p1a1p2a2...pakk,若恒有 g p − 1 p i ≠ 1 ( m o d p ) g^{\frac{p-1}{p_i}} \neq 1(mod~~p) gpip−1=1(mod p)成立,则 g g g为 p p p的原根。
以下代码是寻找 i n t int int范围内单个素数的原根。
ector<int> prime, fac, ans;
bitset<maxn> vis;
void euler() {
vis.reset(), prime.clear();
for (int i = 2; i < maxn; i++) {
if (!vis[i]) prime.push_back(i);
for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
void getFac(int n) {
fac.clear();
int m = sqrt(n + 0.5);
for (int i = 0; i < prime.size() && prime[i] <= m; i++) {
if (n % prime[i] == 0) {
fac.push_back(prime[i]);
while (n % prime[i] == 0) n /= prime[i];
}
}
if (n > 1) fac.push_back(n);
}
ll qkp(ll x, ll n, ll p) {
ll ans = 1;
while (n) {
if (n & 1) ans = ans * x % p;
x = x * x % p;
n >>= 1;
}
return ans;
}
int solve(int p) {
getFac(p - 1);
for (int g = 2; g < p; g++) {
bool ok = 1;
for (int i = 0; i < fac.size(); i++) {
int t = (p - 1) / fac[i];
if (qkp(g, t, p) == 1) {
ok = 0;
break;
}
}
if (ok) return g; //求最小原根
//if (ok) ans.push_back(g); //求所有原根
}
}
m m m为合数
算法思想:
-
判断什么样的数才有原根: { 2 , 4 , p k , 2 ∗ p k ∣ p ∈ p r i m e s , p > 2 } \{2,4,p^k,2*p^k~~|~~~p\in primes,p>2\} {2,4,pk,2∗pk ∣ p∈primes,p>2}
-
找到一个数的最小原根,设为 g g g,则 m m m的所有原根都可以由 g g g的若干次乘方得到,即都能写成 g k ( m o d p ) g^k(mod~~p) gk(mod p)的形式,但是需要注意 g c d ( k , φ ( m ) ) = 1 gcd(k,\varphi(m))=1 gcd(k,φ(m))=1
最小原根的求法:
从小到大枚举 g g g并检验是否为 m m m的原根:对 φ ( n ) \varphi(n) φ(n)质因数分解,即 φ ( m ) = p 1 a 1 p 2 a 2 . . . p a k k \varphi(m)=p_1^{a_1}p_2^{a_2}...p^k_{a_k} φ(m)=p1a1p2a2...pakk,若恒有 g φ ( m ) p i ≠ 1 ( m o d p ) g^{\frac{\varphi(m)}{p_i}} \neq 1(mod~~p) gpiφ(m)=1(mod p)成立,则 g g g为 m m m的原根。找到最小原根后枚举 k ∈ [ 1 , φ ( m ) ] k\in [1,\varphi(m)] k∈[1,φ(m)],对于满足 g c d ( k , φ ( m ) ) = 1 gcd(k,\varphi(m))=1 gcd(k,φ(m))=1,那么 g k m o d m g^k~~mod~~m gk mod m即为其中一个原根。
vector<int> prime, fac, ans;
bitset<maxn> vis, has_root;
ll phi[maxn];
void init() {
vis.reset(), prime.clear();
phi[1] = 1;
for (int i = 2; i < maxn; i++) {
if (!vis[i]) {
prime.push_back(i);
phi[i] = i - 1;
}
for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j]) {
phi[i * prime[j]] = phi[i] * phi[prime[j]];
} else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
has_root.reset();
has_root[2] = has_root[4] = 1;
for (int i = 1; i < prime.size(); i++) {
for (ll j = prime[i]; j < maxn; j *= prime[i]) has_root[j] = 1;
for (ll j = 2 * prime[i]; j < maxn; j *= prime[i]) has_root[j] = 1;
}
}
void getFac(int n) {
fac.clear();
int m = sqrt(n + 0.5);
for (int i = 0; i < prime.size() && prime[i] <= m; i++) {
if (n % prime[i] == 0) {
fac.push_back(prime[i]);
while (n % prime[i] == 0) n /= prime[i];
}
}
if (n > 1) fac.push_back(n);
}
ll qkp(ll x, ll n, ll p) {
ll ans = 1;
x %= p;
while (n) {
if (n & 1) ans = ans * x % p;
x = x * x % p;
n >>= 1;
}
return ans;
}
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b);
}
bool check(int g, int p) {
if (qkp(g, phi[p], p) != 1) return 0;
for (int i = 0; i < fac.size(); i++) {
if (qkp(g, phi[p] / fac[i], p) == 1) return 0;
}
return 1;
}
int solve(int m) {
getFac(phi[m]);
for (int g = 1; g < m; g++) {
if (check(g, m)) return g;
}
}
void getAll(int m) {
int g = solve(m);
ll res = 1;
ans.clear();
for (int i = 1; i <= phi[m]; i++) {
res = res * g % m;
if (gcd(i, phi[m]) == 1) {
ans.push_back(res);
}
}
}