原根
1.定义:
定义
为使得
成立的最小的d(其中a和m互质)
称之为a模m的阶。
阶的性质:
由欧拉定理可知:
当
(记住原根是a,不是d!)
2.原根的性质:
1.具有原根的数字仅有以下几种形式: (p是奇质数)
2.一个数的最小原根的大小不超过
3.若g是m的一个原根,那么
是m的原根的充分必要条件是
,
由此可推知一个数的原根个数为
个
3.求解原根的基本步骤:
- 判断一个数是否有原根。(通过性质1,枚举质数即可)
- 求得最小原根。(通过性质2,依次枚举 判断即可)
- 求出所有原根。(通过性质3,枚举次数d即可)
代码实现:
1.筛出质数并进行第一步(顺便把欧拉函数也筛出来):
void get_prime()
{
is_pri[1]=1;
for(register int i=2;i<N;i++){
if(!is_pri[i]) Prime[++tot]=i,phi[i]=i-1;
for(register int j=1;j<=tot;j++){
if(1ll*i*Prime[j]>=N) break;
register int res=i*Prime[j];
is_pri[res]=1;
if(i%Prime[j]==0){
phi[res]=phi[i]*Prime[j];
break;
}
phi[res]=phi[i]*phi[Prime[j]];
}
}
}
bool judge(int m)//判断原根有无
{
if(m==1) return 0;
if(m==2||m==4) return 1;
if((m&1)==0) m>>=1;if((m&1)==0) return 0;
for(int i=2;i<=tot&&(1ll*Prime[i]*Prime[i]<=m);i++)
{
if(m%Prime[i]!=0) continue;
while(m%Prime[i]==0) m/=Prime[i];
if(m==1) return 1;
return 0;
}
return m;
//这里要return m, 由于Prime[i]*Prime[i]>m即退出的影响
}
2.找出最小原根:
for(int i=2;1ll*i*i<=phi[m];i++){
//筛出phi的约数,用于check
if(phi[m]%i==0){
st[++top]=i;
if(i*i!=phi[m]) st[++top]=phi[m]/i;
}
}
int g;
for(g=2;g<=100;g++)//枚举最小原根
{
if(check(g,m)) break;
}
bool check(int x,int m)
{
if(Pow(x,phi[m],m)!=1) return 0;
for(int i=1;i<=top;i++)if(Pow(x,st[i],m)==1) return 0;
return 1;
}
int Pow(int x,int n,int mod)
{
int ans=1;
while(n){
if(n&1) ans=(1ll*ans*x)%mod;
x=(1ll*x*x)%mod;
n>>=1;
}
return ans;
}
3.找出所有原根
int cnt=0;
register int res=1;
for(register int i=1;i<=phi[m];i++)
//由欧拉定理,只用枚举到g^phi[m]
{
if(cnt==phi[phi[m]]) break;//个数限制
res=(1ll*res*g)%m;
if(gcd(i,phi[m])!=1) continue;
ans[++cnt]=res;
}
sort(ans+1,ans+1+cnt);//由于取了模,要sort
for(register int i=1;i<=cnt;i++)
{
if(i>1) putchar(' ');
printf("%d",ans[i]);
}
puts("");
}