原根 学习笔记

假设 \(\gcd(a,p)=1\),如果 \(a^x\equiv 1\pmod p\),那么最小的 \(x\) 称之为 \(a\) 在模 \(p\) 意义下的阶,记作 \(\delta_p(a)\)

在抽象代数中,这里的“阶”就是模 \(p\) 缩剩余系关于乘法形成的群中,元素 \(a\) 的阶。记号 \(\delta\) 表示阶也只用于这个特殊的群。

性质

  1. \(a^1,a^2,\dots,a^{\delta_p(a)}\)\(p\) 两两不同余。
  2. 如果 \(a^n\equiv 1\pmod p\),那么 \(\delta_p(a) \mid n\)。同时有 \(a^n \equiv a^m \pmod p\) 那么 \(n\equiv m \pmod {\delta_p(a)}\)
  3. \(\delta_p(ab)=delta_p(a) delta_p(b)\) 的充要条件是 \(\gcd(delta_p(a),delta_p(b))=1\)
  4. \(\delta_p(a^k)=\dfrac{\delta_p(a)}{\gcd(\delta_p(a),k)}\)(*)

以上均可以使用定义或者反证法证明。

原根

假设 \(\gcd(a,p)=1\),如果 \(\delta_p(a)=\varphi(p)\),那么 \(a\) 是模 \(p\) 意义下的原根。
也就是说,如果 \(a\) 是模 \(p\) 的原根,那么 \(a^1,a^2,\dots,a^{\varphi(p)}\)\(p\) 两两不同。

判定定理

对于 \(\gcd(a,p)=1\),如果 \(\varphi(p)\) 的所有质因子 \(q\) 都有 \(a^{\varphi(p)/q}\not\equiv 1\pmod p\),那么 \(a\) 是模 \(p\) 意义下的原根。
必要性显然,充分性可以反证。

数量

如果 \(p\) 存在一个原根 \(g\),那么

\[\delta_p(g^k)=\dfrac{\delta_p(g)}{\gcd(\delta_p(g),k)}=\dfrac{\varphi(p)}{\gcd(varphi(g),k)} \]

如果 \(\gcd(\varphi(p),k)=1\),那么 \(g^k\bmod p\) 就是 \(p\) 的原根。
所以 \(p\) 如果有原根,那么就有 \(\varphi(\varphi(p))\) 个。

原根存在定理

\(p\) 存在原根当且仅当 \(p=2,4,q^\alpha,2q^{\alpha}\),其中 \(q\) 为质数,\(\alpha \in \N\)
证明

最小原根

可以证明 \(p\) 如果有原根,那么最小原根是小于 \(p^{0.25}\) 级别的。不过其实当 \(p\) 增大,最小原根不是多项式级别,所以我们可以直接暴力找最小原根。

模板题

Link
找一个数的所有原根。
其实只需要找到最小原根 \(p\),然后只要满足 \(\gcd(\varphi(p),k)=1\),那么 \(g^k\bmod p\) 就是原根。

#include<cstdio>
#include<algorithm>
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template<typename _T>
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define N 1000000
#define maxn 1000039
#define maxm
#define MOD
#define Type int
#ifndef ONLINE_JUDGE
//#define debug
#endif
using namespace std;
Type read(){
	char c=gc(); Type s=0; int flag=0;
	while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
	while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
	if(flag) return -s; return s;
}
int isp[maxn],pr[maxn],phi[maxn],minx[maxn],cnt;
void init(){
	int i,j; for(i=2;i<=N;i++) minx[i]=INF;
	for(i=2;i<=N;i++){
		if(!isp[i]) pr[++cnt]=i,phi[i]=i-1,minx[i]=i;
		for(j=1;j<=cnt&&i*pr[j]<=N;j++){
			isp[i*pr[j]]=1; minx[i*pr[j]]=mmin(minx[i*pr[j]],mmin(pr[j],minx[i]));
			if(i%pr[j]==0){ phi[i*pr[j]]=phi[i]*pr[j]; break; }
			phi[i*pr[j]]=phi[i]*(pr[j]-1);
		}
	} phi[1]=1,isp[1]=1; return;
}
int n,d,ans[maxn],num;
int check(int x){
	if(x==2||x==4) return 0;
	if(x%2==0) x/=2; if(x%2==0) return 1;
	int i=minx[x]; while(x%i==0) x/=i;
	return x!=1;
}
int gcd(int x,int y){ if(!x||!y) return x|y; return gcd(y,x%y); }
int fastpow(int x,int y){
	int res=1,tmp=x;
	while(y){
		if(y&1) res=1ll*res*tmp%n;
		y>>=1,tmp=1ll*tmp*tmp%n;
	} return res;
}
int js(int x){
	if(gcd(x,n)!=1) return 0;
	int cur,tmp,las; cur=tmp=phi[n],las=-1;
	while(cur!=1){
		while(cur!=1&&minx[cur]==las) cur/=minx[cur];
		if(cur==1) return 1;
		if(fastpow(x,tmp/minx[cur])==1) return 0;
		las=minx[cur]; cur/=minx[cur];
	} return 1;
}
void work(){
	n=read(); d=read(); if(check(n)){ pc('0'),pc('\n'),pc('\n'); return; }
	int i,g; num=0; for(i=1;i<n;i++) if(js(i)){ g=i; break; }
	for(i=1;i<=phi[n];i++) if(gcd(i,phi[n])==1) ans[++num]=fastpow(g,i);
	sort(ans+1,ans+num+1); print(num),pc('\n');
	for(i=d;i<=num;i+=d) print(ans[i]),pc(' '); pc('\n'); return;
}
int main(){
	freopen("1.in","r",stdin);
	//freopen(".out","w",stdout);
	init(); int T=read(); while(T--) work(); return 0;
}
posted @ 2023-02-28 20:30  jiangtaizhe001  阅读(30)  评论(0编辑  收藏  举报