原根和阶及模板
小记:
摆出定义和算法 ⇒ \Rightarrow ⇒ 解释算法
主要参考 算法导论第31章 和传送门1
原根的定义
若 o r d n ( g ) = ϕ ( n ) ord_n(g)=\phi(n) ordn(g)=ϕ(n), 即g对模n的阶等于n的欧拉函数,则g是n的原根。
如果g是n的原根,则对于 Z n ∗ Z_n^{*} Zn∗的任意一个元素,都存在z,使得 g z ≡ a ( m o d n ) g^z \equiv a(mod n) gz≡a(modn)
什么样的N有原根
如果N=2,4, p e , 2 p e p^e ,2p^e pe,2pe(p是大于2的质数,e是正整数),则N有原根
原根的意义?
如果 Z n ∗ Z_n^{*} Zn∗包含一个原根,就称 Z n ∗ Z_n^{*} Zn∗是循环的。
求N的原根的算法步骤
step1: 将N的欧拉函数phi进行质因数分解,phi(N)= ϕ ( N ) \phi(N) ϕ(N)=$ p_1^{a_1} *p_2{a_2}*……p_n{a_n}$
step2: 枚举i,判断每个i是否有 i k i^{k} ik mod N = = 1 的 最 小 k 是 不 是 ==1的最小k是不是 ==1的最小k是不是 ϕ ( N ) p i \frac{\phi(N)}{p_i} piϕ(N) ,符合条件的就是N的最小原根
step3:利用最小原根得到 其他原根,
其他原根的形式都为 g k g^k%N gk形式,且gcd(k, ϕ ( N ) \phi(N) ϕ(N))=1
基础知识 🥛
1.模运算
Z n Z_n Zn={0,1,……,n-1}
Z n ∗ Z_n^{*} Zn∗={ a ∈ Z n a\in Z_n a∈Zn 且 gcd(a,n)=1}
比如 Z 15 ∗ Z_{15}^{*} Z15∗={1, 2, 4, 7, 8, 11, 13, 14}
2. ϕ ( n ) \phi(n) ϕ(n)是什么?
Z n ∗ Z_n^{*} Zn∗的规模就是欧拉函数, ∣ Z n ∗ ∣ = ϕ ( n ) = n ∗ ∏ p i 是 素 数 且 p i ∣ n ( 1 − 1 p ) |Z_n^{*}|= \phi(n)=n*\prod_{p_i是素数且p_i|n}(1-\frac{1}{p}) ∣Zn∗∣=ϕ(n)=n∗∏pi是素数且pi∣n(1−p1)
比如 ϕ ( 15 ) = 8 \phi(15)=8 ϕ(15)=8
3.ord()是什么?
a通过(自定义运算 a^k%n,k为正整数)得到有限群。
一个元素的阶等于它所生成子群的规模,即 ord(a)=|<a>|
也是 满足 a k % n = 1 a^{k}\%n=1 ak%n=1的最小正整数k。
举例如下
n=7时, Z 7 ∗ Z_7^{*} Z7∗={1,2,3,4,5,6}
n=7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
2 i % n 2^i\%n 2i%n | 1 | 2 | 4 | 2 | 4 | 1 | 2 |
3 i % n 3^i\%n 3i%n | 1 | 3 | 2 | 6 | 4 | 5 | 1 |
如 n=7,i=2时,<a>={1,2,4}, a 3 = 1 a^3=1 a3=1, |a|=3
n=7, i=3时,<a>={1,3,2,6,4,5}, a 6 = 1 a^6=1 a6=1, |a|=6
发现了吗?这时<a>与 Z 7 ∗ Z_7^{*} Z7∗同,故3是7的原根
4.欧拉定理
如果n是正整数且 a ∈ Z n ∗ a\in Z_n^{*} a∈Zn∗, 那么 a ϕ ( n ) % n = 1 a^{\phi(n)}\%n=1 aϕ(n)%n=1。
算法解释
那么回到原根定义, o r d n ( g ) = ϕ ( n ) ord_n(g)=\phi(n) ordn(g)=ϕ(n)
欧拉函数很好求,而g要怎么寻找呢?
我们采用枚举i的方式,对每个i,
看使它为 i^k %n=1的最小正整数k是不是 ϕ ( n ) \phi(n) ϕ(n)
并且如果 i ∈ Z n ∗ i\in Z_n^{*} i∈Zn∗ ,那么由 欧拉定理 得到$ i^{\phi(n)}%n=1$,如果不属于的话,我们还要去计算证明一下。
那难道要用 i k i^k ik,k ∈ \in ∈ [1, ϕ ( n ) \phi(n) ϕ(n)) 去检查吗?
不用,只需要枚举 i ϕ ( N ) p i i^{\frac{\phi(N)}{p_i}} ipiϕ(N) %n去就行,
原因是k 分为2部分,不能被 ϕ ( n ) \phi(n) ϕ(n)整除的,和能被 ϕ ( n ) \phi(n) ϕ(n)整除的。
对于不能被 ϕ ( n ) \phi(n) ϕ(n)整除部分
关于不能被 ϕ ( n ) \phi(n) ϕ(n)整除,
使用结论,若 i g % m o d = 1 i^g\%mod=1 ig%mod=1,则对于任意不满足c | g的数不存在 i c % m o d = 1 i^c\%mod=1 ic%mod=1,可以直接pass。
证明:
使
用
反
证
法
使用反证法
使用反证法
假
设
存
在
不
满
足
c
∣
g
使
i
c
%
m
o
d
=
1
,
且
c
1
是
最
小
的
c
假设存在不满足c|g使\,i^{c}\%\,mod=1,且c_1是最小的c
假设存在不满足c∣g使ic%mod=1,且c1是最小的c
∴
i
c
1
%
m
o
d
=
1
,
c
2
=
g
−
c
1
\therefore i^{c_1}\,\%\,mod=1,\,c_2=g-c_1
∴ic1%mod=1,c2=g−c1
∵
i
g
%
m
o
d
=
1
,
我
们
在
前
面
已
经
确
认
过
\because i^g\%mod=1,我们在前面已经确认过
∵ig%mod=1,我们在前面已经确认过
∴
(
i
c
1
%
m
o
d
)
∗
(
i
c
2
%
m
o
d
)
=
1
\therefore(i^{c_1}\%mod)*(i^{c_2}\%mod)=1
∴(ic1%mod)∗(ic2%mod)=1
∴
i
c
2
%
m
o
d
=
1
\therefore i^{c_2}\%mod=1
∴ic2%mod=1
如果 c_1|c_2,则
g
=
c
1
+
c
2
=
(
1
+
f
)
c
1
g=c_1+c_2=(1+f)c_1
g=c1+c2=(1+f)c1,与假设条件相矛盾
∴
g
c
d
(
c
1
,
c
2
)
<
c
1
\therefore gcd(c1,c2)<c_1
∴gcd(c1,c2)<c1
又
∵
\because
∵
i
c
2
−
c
1
≡
i
c
2
∗
(
i
c
1
)
−
1
≡
1
(
%
m
o
d
)
i^{c_2-c_1}\equiv i^{c_2}*(i^{c_1})^{-1}\equiv1(\% mod)
ic2−c1≡ic2∗(ic1)−1≡1(%mod)
∴
\therefore
∴由gcd的更相减损术原理得
i
g
c
d
(
c
2
,
c
1
)
i^{gcd(c_2,c_1)}
igcd(c2,c1)%mod=1
又
∵
g
c
d
(
c
1
,
c
2
)
<
c
1
与
原
假
设
c
1
是
最
小
相
矛
盾
又\because gcd(c1,c2)<c1 与原假设c_1是最小相矛盾
又∵gcd(c1,c2)<c1与原假设c1是最小相矛盾
∴
假
设
不
成
立
,
故
若
i
g
%
m
o
d
=
1
,
不
满
足
c
∣
g
,
则
不
能
使
得
i
c
%
m
o
d
=
1
\therefore 假设不成立,故若 i^g\%mod=1,不满足c|g,则不能使得i^c\%mod=1
∴假设不成立,故若ig%mod=1,不满足c∣g,则不能使得ic%mod=1
对于能被 ϕ ( n ) \phi(n) ϕ(n)整除部分
把 ϕ ( n ) \phi(n) ϕ(n)进行质因数分解,得到 ϕ ( n ) = p 1 a 1 ∗ p 2 a 2 ∗ … … \phi(n)=p_1^{a_1}*p_2^{a_2}*…… ϕ(n)=p1a1∗p2a2∗……
只要 i p d % n ≠ 1 i^{p_d}\% n\neq 1 ipd%n=1,
那么如果 i p d ∗ … … i p d ⏟ ϕ ( n ) p d ≠ 1 \underbrace{i^{p_d}*……i^{p_d}}_{\frac{\phi(n)}{p_d}} \neq1 pdϕ(n) ipd∗……ipd=1,即只要证 i ϕ ( N ) p d i^{\frac{\phi(N)}{p_d}} ipdϕ(N) %n$\neq$1即可
代码
(hdu4492模板)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10,inf=0x3f3f3f3f;
int n,fac[N],nf;
vector<int> ans;
int Pow(int x,int p,int mod) {
int ret=1;
for(; p; p>>=1,x=(ll)x*x%mod)if(p&1)ret=(ll)ret*x%mod;
return ret;
}
int phi(int x) { //求欧拉函数
int ret=x;
for(int i=2; i<=x/i; ++i)if(x%i==0) {
ret=ret/i*(i-1);
while (x%i==0)x/=i;
}
if(x>1)ret=ret/x*(x-1);
return ret;
}
void getfac(int x) {
//得到x的质因数
nf=0;
for(int i=2; i<=x/i; ++i)if(x%i==0)
for(fac[nf++]=i; x%i==0; x/=i);
if(x>1)fac[nf++]=x;
}
bool is_have_root(int x) {
//如果N=2,4,p^e ,2*(p^e)(p是大于2的质数,e是正整数),则N有原根
if(x==2||x==4)return 1;
if(x%4==0)return 0;
if(x%2==0)x/=2;
for(int i=3; i*i<=x; ++i)if(x%i==0) {
for(; x%i==0; x/=i);
return x==1;
}
return 1;
}
int minRoot(int x,int phix) {
//求最小原根
if(x==2||x==4)return x-1;
for(int i=2; i<x; ++i)if(Pow(i,phix,x)==1) {
bool flag=1;
for(int j=0; j<nf; ++j)if(Pow(i,phix/fac[j],x)==1) {flag=0; break;}
if(flag)return i;
}
}
void solve(int x) {
int phix=phi(x); //得到x的欧拉函数
getfac(phix); //得到phi(x)的质因数
int r=minRoot(x,phix); //得到最小原根
ans.clear(),ans.push_back(r);
for(int i=2; i<phix; ++i)if(__gcd(i,phix)==1)ans.push_back(Pow(r,i,x)); //用最小原根得到其它原根
sort(ans.begin(),ans.end());
}
int main() {
// freopen("in.text","r",stdin);
while(scanf("%d",&n)==1) {
if(!is_have_root(n))puts("-1");
else {
solve(n);
for(int i=0; i<ans.size(); ++i)printf("%d%c",ans[i]," \n"[i==ans.size()-1]);
}
}
return 0;
}