知识点 - 原根

知识点 - 原根

解决问题类型:

  1. 求原根(最小的/全部的)
  2. 高次同余方程要用
  3. NTT要用

定义与结论

定理6.2.1 ( Z p ∗ , ⋅ ) (\mathbb{Z}_p^*,\cdot) (Zp,) 是循环群,即存在 a ∈ Z p ∗ a\in \mathbb{Z}_p^* aZp ,使得
Z p ∗ = { a n ∣ n = 1 , 2 , … , p − 1 } \mathbb{Z}_p^*=\{a^n|n=1,2,\dots,p-1\} Zp={ann=1,2,,p1}
这样的 a a a 称为 p p p 的原根。

性质
  1. 素数一定有原根(貌似duanLS口误),原根不唯一,部分合数也有原根(对非素数n来说,它的原根 g g g可以让 g k g^k%n gk 产生所有与 n n n互质的数)
  2. 原根一般不大,暴力枚举即可
  3. n = 1 , 2 , 4 , p k , 2 p k n=1,2,4,p^k,2p^k n=1,2,4,pk,2pk.时才有
  4. n n n为质数时, g k g^k%n gk取遍 [ 1 , n − 1 ] [1,n-1] [1,n1]
  5. ϕ ( n ) \phi(n) ϕ(n)是最小的使 g ϕ ( n ) ≡ 1 ( m o d n ) g^{\phi (n)} \equiv 1 \pmod n gϕ(n)1(modn) 数当且仅当g是n的原根
  6. ϕ ( ϕ ( n ) ) \phi (\phi (n) ) ϕ(ϕ(n))为n的原根数量
  7. g g g is O ( log ⁡ 6 p ) O(\log^6 p) O(log6p).
  8. 1000000007的原根为5,998244353的原根为3
  9. 如果g为n的原根,则 g d g^d gd为n的原根的充要条件是 ( d , φ ( n ) ) = 1 (d,φ(n))=1 (d,φ(n))=1(例题用到的结论)

复杂度(找原根模板):

我们先分解 ϕ ( n ) = p 1 a 1 ⋯ p s a s \phi (n) = p_1 ^ {a_1} \cdots p_s ^ {a_s} ϕ(n)=p1a1psas,如果所有 g ϕ ( n ) p i ( m o d n ) g ^ { \frac {\phi (n)} {p_i}} \pmod n gpiϕ(n)(modn)都不等于1,那么 g g g就是原根

O ( A n s ⋅ log ⁡ ϕ ( n ) ⋅ log ⁡ n ) O(Ans \cdot \log \phi (n) \cdot \log n) O(Anslogϕ(n)logn) A n s = g Ans=g Ans=g 的数量级是 O ( log ⁡ 6 p ) O(\log^6 p) O(log6p).原根一般不大,暴力枚举即可

int getPrimitiveRoot(int p) {
	vector<int> fact;
	int phi = p-1,  n = phi;
	for (int i=2; i*i<=n; ++i)
		if (n % i == 0) {
			fact.push_back (i);
			while (n % i == 0)
				n /= i;
		}
	if (n > 1)
		fact.push_back (n);
 
	for (int res=2; res<=p; ++res) {
		bool ok = true;
		for (size_t i=0; i<fact.size() && ok; ++i)
			ok &= kpow (res, phi / fact[i], p) != 1;
		if (ok)  return res;
	}
	return -1;
}

例题

求n的所有原根

代码

//https://www.cnblogs.com/fenghaoran/p/7110296.html
inline void prepare()
{
  phi[1]=1;
  for(int i=2;i<N;++i){
    if(!vis[i])P[++tot]=i,phi[i]=i-1;
    for(int j=1;j<=tot;++j){
      if(i*P[j]>=N)break;
      vis[i*P[j]]=1;
      if(i%P[j])phi[i*P[j]]=phi[i]*phi[P[j]];
      else{phi[i*P[j]]=phi[i]*P[j];break;}
    }
  }
}
 
inline int QPow(int d,int z,int Mod)
{
  int ans=1;
  for(;z;z>>=1,d=1ll*d*d%Mod)if(z&1)ans=1ll*ans*d%Mod;
  return ans;
}
 
inline bool check(int x)
{
  if(x==2 || x==4)return 1;
  if((x&1)^1)x>>=1;
  for(int i=2;P[i]<=x;++i)
    if(x%P[i]==0){
      while(x%P[i]==0)x/=P[i];
      return x==1?P[i]:0;
    }
  return 0;
}
 
inline int get_rg(int fx)
{
  int pt[1010],tt=0,Txd=phi[fx];
  for(int i=1;P[i]*P[i]<=Txd;++i)
    if(Txd%P[i]==0){
      pt[++tt]=P[i];
      while(Txd%P[i]==0)Txd/=P[i];
    }
  if(Txd!=1)pt[++tt]=Txd;
  for(int i=2;i<=fx;++i)
    if(QPow(i,phi[fx],fx)==1){
      int flag=1;
      for(int j=1;j<=tt;++j)
    if(QPow(i,phi[fx]/pt[j],fx)==1){
      flag=0;break;
    }
      if(flag)return i;
    }
  return 0;
}
 
inline void work(int fx)
{
  int tt=0,pr[N];
  if(fx==2){printf("1\n");return;}
  if(fx==4){printf("3\n");return;}
  int T=check(fx);
  if(!T){printf("-1\n");return;}
  int g=get_rg(fx);
  for(int i=1,k=g;i<phi[fx];++i,k=1ll*k*g%fx)
    if(gcd(i,phi[fx])==1)
      pr[++tt]=k;
  sort(pr+1,pr+tt+1);
  for(int i=1;i<tt;++i)
    printf("%d ",pr[i]);
  printf("%d",pr[tt]);
  printf("\n");
}
 
int main()
{
  prepare();
  while(scanf("%d",&n)!=EOF)work(n);
  return 0;
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Best KeyBoard

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值