自己习惯的一些定义,可能和其他地方不一样
O r d p ( a ) Ord_p(a) Ordp(a): a a a在模 p p p意义下的阶,念作 o r d e r order order
ϕ ( p ) \phi(p) ϕ(p): p p p的欧拉函数值,念作 f a i fai fai
R t ( p ) Rt(p) Rt(p):模 p p p意义下的原根,念作 r o o t root root
I r ( a ) I_r(a) Ir(a):模 p p p意义下, a a a对原根 r r r的指标(离散对数),念作 i n d e x index index
定义: 最小的 t t t使得: a t ≡ 1 ( m o d    p ) a^t\equiv1(mod \;p) at≡1(modp),则称 t t t为 a a a在模 p p p下的阶( ( a , p ) = 1 (a,p)=1 (a,p)=1),记作: O r d p ( a ) = t Ord_p(a)=t Ordp(a)=t
理解: a % p a\%p a%p下的阶 t t t相当于 a % p a\%p a%p的循环节,每乘上 a t a^t at,值不变。
知识: 欧拉定理有: a ϕ ( p ) ≡ 1 a^{\phi(p)}\equiv 1 aϕ(p)≡1,所以有: t ∣ ϕ ( p ) t|\phi(p) t∣ϕ(p)
定义: 若 O r d p ( a ) = ϕ ( p ) Ord_p(a)=\phi(p) Ordp(a)=ϕ(p),则称这样的 a a a为模数 p p p的原根,记作 R t ( p ) = a Rt(p)=a Rt(p)=a
理解: 对于模数 p p p,找到一些数 a a a,满足 a a a的次方循环节恰好为 ϕ ( p ) \phi(p) ϕ(p),这些数就是 p p p的原根
知识:
求解:
验证一个数 a a a是否为 R t ( p ) Rt(p) Rt(p),我们可以通过试除法。显然 a ϕ ( p ) ≡ 1 a^{\phi(p)}\equiv 1 aϕ(p)≡1,那么我们现在只需要证明最小循环节为 ϕ ( p ) \phi(p) ϕ(p)即可,而如果存在更小的循环节,则一定为 ϕ ( p ) \phi(p) ϕ(p)的因子。
所以我们枚举 ϕ ( p ) \phi(p) ϕ(p)的所有素因子 d d d,看看 ϕ ( p ) / d \phi(p)/d ϕ(p)/d是否是循环节即可。
定义: 设 r r r为 R t ( p ) Rt(p) Rt(p),有 r x ≡ n ( m o d    p ) r^x\equiv n(mod\;p) rx≡n(modp),则称 x x x为以 r r r为底, n n n的离散对数,记作: x = I r ( n ) x=I_r(n) x=Ir(n)
理解: 结合数学中的对数去理解会方便很多,很多地方都类似。唯一需要注意的是离散对数间的运算,模数需要转化为 ϕ ( p ) \phi(p) ϕ(p)。
知识:
求解:
即求出 r x ≡ n ( m o d    p ) r^x\equiv n(mod\;p) rx≡n(modp)。先枚举法求出 p p p的原根 r r r,再用BSGS算法求出以 r r r为底的 n n n的离散对数 x x x。
求出 P − 1 P-1 P−1的素因子,从 2 2 2开始枚举 i i i,判断是否存在 x = P − 1 d ( d ∣ ( P − 1 ) ) → i x ≡ 1 ( m o d    P ) x=\frac{P-1}{d}(d|(P-1))\to i^x\equiv 1(mod \;P) x=dP−1(d∣(P−1))→ix≡1(modP)
/*
* Author : Jk_Chen
* Date : 2019-08-20-18.53.11
*/
#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
const LL mod=1e9+7;
const int maxn=4e4+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/
int pri[maxn],ct;
bool vis[maxn];
void init(){
rep(i,2,maxn-1){
if(!vis[i])pri[++ct]=i;
for(int j=1;j<=ct&&pri[j]*i<maxn;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0)break;
}
}
}
LL Pow(LL a,LL b,LL mod){
LL res=1;
while(b){
if(b&1)res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void get(int n,vector<int>&fac){
n--;
for(int i=1;i<=ct&&pri[i]<=n;i++){
if(n%pri[i]==0)fac.push_back(pri[i]);
}
}
int main(){
init();
int n;
while(cin>>n){
vector<int>fac;
get(n,fac);
for(int i=2;i<n;i++){
int can=1;
for(auto P:fac){
if(Pow(i,(n-1)/P,n)==1){
can=0;break;
}
}
if(can){
printf("%d\n",i);
break;
}
}
}
return 0;
}
根据定理,原根个数为 ϕ ( ϕ ( P ) ) \phi(\phi(P)) ϕ(ϕ(P)),而素数 P P P的欧拉函数值为 P − 1 P-1 P−1,所以求出 ϕ ( P − 1 ) \phi(P-1) ϕ(P−1)即可。
/*
* Author : Jk_Chen
* Date : 2019-08-20-18.53.11
*/
#include
#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
const LL mod=1e9+7;
const int maxn=65536;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/
int pri[maxn],ct;
bool vis[maxn];
int phi[maxn];
void Get_phi(){
phi[1] = 1;
for(int i=2; i<maxn; i++) {
if(!vis[i]) {
pri[++ct] = i;
phi[i] = i-1;
}
for(int j=1; j<=ct&&i*pri[j]<maxn; j++){
vis[i*pri[j]] = 1;
if(i%pri[j] == 0){
phi[i*pri[j]] = pri[j] * phi[i];
break;
}
else phi[i*pri[j]] = (pri[j]-1) * phi[i];
}
}
}
int main(){
Get_phi();
int n;
while(cin>>n){
printf("%d\n",phi[n-1]);
}
return 0;
}