本博客参考于:https://www.cnblogs.com/cytus/p/9296661.html
阶
阶的定义:设 m > 1,且gcd(a,m) = 1,那么使得 ar ≡ 1(mod m) 成立最小的正整数 r 称为,a 对模 m 的阶,记作 δm(a)。
相关定理:
定理一:
若 m > 1,并且gcd(a,m) = 1,又满足 an ≡ 1 (mod m),则 δm(a) | n。
定理二:
由定理一可推得:δm(a) | ϕ(m)。
证明:有欧拉定理得 aϕ(m) ≡ 1(mod m)可知,δm(a) <= ϕ(m),再由定理一即得证。
相关推论:
推论一:
若p和q为奇素数,且q | (ap−1), 则要么有q | (a−1),要么有q = 2kp+1,其中 k 为某整数。
推论一:
2p-1 的任何因子必取 2kp+1 的形式,其中 k 为某整数。
原根
原根的定义:设 m 为正整数,a 为整数,如果 a 对模 m 的阶等于ϕ(m),那么称 a 为模 m 的一个原根。( aϕ(m) ≡ 1(mod m) )
相关定理:
定理一:
一个正整数m有原根的充要条件是m = 2,4,pe,2pe,其中,p奇素数,e为正整数。
定理二:
每一个素数 p 都有 ϕ(p−1) 个原根,事实上,每一个正整数 m 都有 ϕ(ϕ(m)) 个原根。
定理三:
若g是m的一个原根,则g,g2,…… ,gϕ(m)各数对m取模的非负最小剩余就是小于m且与m互质的ϕ(m)个数的一个排列。
原根的求法
方法一:
质数的原根 = ϕ(p−1)。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 300;
int pri[maxn],tot;
bool v[maxn];
void primes() //线性筛素数
{
tot = 0;
memset(v,true,sizeof(v));
for(int i=2;i<maxn;i++){
if(v[i])
pri[++tot] = i;
for(int j=1;j<=tot&&pri[j]*i<maxn;j++){
v[i*pri[j]] = false;
if(i%pri[j]==0) break;
}
}
}
int phi(int n) //欧拉函数
{
int ans = 1;
for(int i=1;pri[i]*pri[i]<=n;i++){
if(n%pri[i]==0){
ans *= (pri[i]-1);
n /= pri[i];
while(n%pri[i]==0){
ans *= pri[i];
n /= pri[i];
}
}
}
if(n>1)ans *= (n-1);
return ans;
}
int main(void)
{
int n;
primes();
while(scanf("%d",&n)!=EOF){
printf("%d\n",phi(n-1));
}
return 0;
}
方法二:
正整数的原根求解:ϕ(ϕ(m))
这里就演示一下打表……
#include<bits/stdc++.h>
using namespace std;
using namespace std;
const int N = 2000010;
const int M = 1000010;
const int inf = 2000010;
bool v[N],vv[N]; //v代表是否是素数,vv代表是否原根
int phi[N],prime[M],m; //phi 欧拉函数,prime 素数表
void pri_r()
{
m = 0;
prime[++m]=2;v[4]=1;
phi[2]=1;phi[4]=2;phi[1]=1;
for(int i=3;i<=inf;i++){
if(!v[i]){
prime[++m] = i;
phi[i] = i-1;
vv[i] = 1;
}
for(int j=1;j<=m&&i*prime[j]<=inf;j++){
v[i*prime[j]] = 1;
if(i%prime[j]==0){
vv[i*prime[j]]=vv[i];
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else phi[i*prime[j]] = phi[i]*(prime[j]-1);
}
}
vv[2] = vv[4]= 1;
}
int main()
{
pri_r();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
if(vv[x]||(!(x&1)&&vv[x>>1]&&x!=8)) //
printf("%d\n",phi[phi[x]]);
else
puts("0"); //没有原根
}
return 0;
}
判断一个数是不是m的原根:
方法一:
首先求ϕ(m)的素幂分解式:
ϕ(m)=p1e1 ∗ p2e2 ∗ … ∗ pkek
然后枚举g,若恒满足
gϕ(m)/pi ≠ 1(modm),其中i=1,2,…,k
则g是m的一个原根。
方法二:
如果m是质数:
首先求ϕ(m-1)的素幂分解式:
ϕ(m-1)=p1e1 ∗ p2e2 ∗ … ∗ pkek
然后枚举g,若恒满足
g(m-1)/pi ≠ 1(modm),其中i=1,2,…,k
则g是m的一个原根。
例题:
http://www.51nod.com/Challenge/Problem.html#problemId=1135
1135 原根
设m是正整数,a是整数,若a模m的阶等于φ(m),则称a为模m的一个原根。(其中φ(m)表示m的欧拉函数)
给出1个质数P,找出P最小的原根。
输入样例
3
输出样例
2
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 1000010;
const ll lnf = 0x3f3f3f3f3f3f3f;
int pri[maxn],tot;
bool v[maxn];
int p[maxn],cnt;
void primes()
{
tot = 0;
memset(v,true,sizeof(v));
for(int i=2;i<maxn;i++){
if(v[i])
pri[++tot] = i;
for(int j=1;j<=tot&&pri[j]*i<maxn;j++){
v[i*pri[j]] = false;
if(i%pri[j]==0) break;
}
}
}
void phi(int n)
{
for(int i=1;pri[i]*pri[i]<=n;i++){
if(n%pri[i]==0){
p[++cnt] = pri[i];
n /= pri[i];
while(n%pri[i]==0){
n /= pri[i];
}
}
}
if(n>1){
p[++cnt] = n;
}
}
int ksm(int a,int b,int c)
{
int ans = 1;
for(int i=b;i;i>>=1){
if(i&1) ans = 1ll*ans*a % c;
a = 1ll*a*a % c;
}
return ans%c;
}
bool judge(int g,int m)
{
for(int i=1;i<=cnt;i++){
int gm = ksm(g,(m-1)/p[i],m);
if(gm==1) return false;
}
return true;
}
int main(void)
{
int n;
primes();
scanf("%d",&n);
cnt = 0;phi(n-1);
for(int i=2;i<n;i++){
if(judge(i,n)){
printf("%d\n",i);
break;
}
}
return 0;
}