本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【数论数学】原根与阶
定义
设 a,m 两数互质,满足 a^x^≡1(mod m) 的最小的 x,称为 a 对 m 的阶,记为 ordm(a)。
当 ordm(a)=ϕ(m) 时称为 a 为 m 的一个原根。
性质
- n有原根⇔(p为奇素数)
- 一个数的最小原根的大小不超过
- 若g是m的一个原根,那么g^d^是m的原根的充分必要条件是gcd(d,Φ(m))=1,由此可推知一个数的原根个数为Φ(Φ(m))个
- (利用这个性质可以求出所有原根)
- 构成摸m的既约剩余系
- (mod m)
- 设 ,则g是m的原根⇔对与所有的pi,(mod m)
求解
- 判断一个数是否有原根。(枚举质数)
- 求得最小原根。(依次枚举 判断)
- 求出所有原根。(枚举次数d)
代码
题目链接
代码
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int N=1000000;
bool f[1000000];
int phi(int x)
{
if (f[x]) return x-1;
int ans=x;
for (int i=2;i<=x;i++)
if (x%i==0)
{
while (x%i==0) x/=i;
ans=ans-ans/i;
}
return x>1?ans-ans/x:ans;
}
int gcd(int a,int b)
{
swap(a,b);
int c=a%b;
while (c) a=b,b=c,c=a%b;
return b;
}
int quick_mod(int x,int p,int mod)
{
long long s=1,a=x;
while (p)
{
if (p&1) s=(s*a)%mod;
a=a*a%mod,p>>=1;
}
return (int)s;
}
vector<int> V,G;
void cal(int x)
{
G.clear();
if (f[x]) return;
else for (int i=2;i*i<=x;i++)
if (x%i==0)
{
G.push_back(i);
if (i*i!=x) G.push_back(x/i);
}
}
bool exist(int n)
{
if (n%2==0) n/=2;
if (f[n]) return 1;
for (int i=3;i*i<=n;i+=2){
if (n%i==0){
while (n%i==0) n/=i;
return n==1;
}
}
return 0;
}
bool solve(int n)
{
if (n==4||n==2) return printf("%d\n",n-1),0;
if (!exist(n)) return printf("-1\n");
int p=phi(n),x=-1;
cal(p);
for (int i=2;i<n;i++)
if (quick_mod(i,p,n)==1)
{
bool flag=1;
for (int j=0;j<G.size()&&flag;j++)
if (quick_mod(i,G[j],n)==1) flag=0;
if (flag)
{
V.resize(1),V[0]=x=i;
break;
}
}
if (x==-1) return printf("-1\n"),0;
for (int i=2;i<p;i++)
if (gcd(i,p)==1) V.push_back(quick_mod(x,i,n));
sort(V.begin(),V.end());
vector<int>::iterator it=unique(V.begin(),V.end());
V.erase(it,V.end());
for (int i=0;i<V.size();i++)
{
if (i) printf(" ");
printf("%d",V[i]);
}
return printf("\n"),0;
}
int main()
{
memset(f,1,sizeof(f));
f[0]=f[1]=0;
for (int i=2;i<1000000;i++)
if (f[i])
for (int j=i<<1;j<1000000;j+=i) f[j]=0;
int n;
while (~scanf("%d",&n)) solve(n);
return 0;
}