原根
原根
阶:设 \(a, m \in \mathbb{N}^{+},\) 且 \(a \perp m,\) 使 \(a^{x} \equiv 1(\bmod m)\) 成立的最小正整数 \(x,\) 称为 \(a\) 模 \(m\) 的阶, 记为 \(\operatorname{ord}_{m} a_{0}\)
原根:设 \(g, m \in \mathbb{N}^{+},\) 且 \(g \perp m ;\) 若 \(\operatorname{ord}_{m} g=\varphi(m),\) 则称 \(g\) 是模 \(m\) 的原根。
定义原根的意义在于, 原根非常类似于单位根, 故有一些很好的性质, 比如说我们考虑当 \(m\) 是质数时, 原根有如下等价定义:若 \(g\) 是模 \(m\) 的原根, 则 \(g^{1}, g^{2}, \cdots, g^{m-1}\) 的值在模 \(m\) 的意义下两两不同, 注意到模\(m\)剩余系中恰有\(m\)个数,而任意一个非零数的幂次都不可能等于0,故原根的幂次在模\(m\)的剩余系中是最稠密的,换句话说,仅用原根就能够充分表示模 \(m\) 剩余系的性质。
个数性质
如果模m有原根,那么原根恰有$ \varphi(\varphi(m))$个。
一个比较通俗的证明:
剩余系是加法群,约化剩余系是乘法群。
在剩余系中,可逆元是与模数 m互质的数(可能不是叫可逆元,反正大概了解一下定义就行),所以其个数为 \(\varphi(m),\) 并且可逆元在加法的意义上可以取遍剩余系, 也就是说如果令 \(q\) 为可逆元, 那么 \(q, 2 q, \cdots, m q\) 在模 \(m\) 的意义下可以取遍整个剩余系。
约化剩余系由可逆元所组成的,并且原根在乘法的意义上可以取遍约化剩余系,也就是说假设 \(g\) 为模 \(m\) 意义下的一个原根, 那么 \(g^{1}, g^{2}, \cdots, g^{\varphi(m)}\) 在模 \(m\) 的意义下可以取遍整个约化剩余系, 可见原根一定 属于约化剩余系, 所以我们首先应该在约化剩余系的 \(\varphi(m)\) 个数里寻找。
观察 \(g^{1}, g^{2}, \cdots, g^{\varphi(m)},\) 我们想知道其中有多少数是能在乘法的意义下取遍模 \(m\) 的整个约化剩余系, 根据乘法中指数相加以及扩展欧拉定理, 我们可以将问题转化成求 \(0,1, \cdots, \varphi(m)-1\) 中有多少数在 加法的意义下能取遍模 \(\varphi(m)\) 的剩余系, 这样就变成了求模 \(\varphi(m)\) 的剩余系中可逆元的个数, 也就是说 模 \(m\) 的意义下原根的个数为 \(\varphi(\varphi(m))\) 个。
存在定理
定理 1: 质数 \(P\) 一定有原根, 且恰有 \(\varphi(P-1)\) 个原根,实际上, 当且仅当 \(m=2,4, P^{k}, 2 P^{k}\) 时(k为任意正整数), \(m\) 才有原根。
判定定理
若 \(g\) 为模 \(m\) 的原根, 则对于任意 \(\varphi(m)\) 的质因子 \(p,\) 必有 \(g^{\frac{\varphi(m)}{p}} \not \equiv 1(\bmod m)\)
求所有原根
定理:设 \(g\) 为模 \(m\) 的原根, 则集合 \(S=\left\{g^{s} \mid 1 \leq s \leq \varphi(m), s \perp \varphi(m)\right\}\) 是模 \(m\) 的所有原根。
可以证明, 最小原根是不大于 \(\sqrt[4]{m}\) 级别的。因此, 不妨枚举 \([1, \sqrt[4]{m}]\) 的整数, 得到最小原根 \(g\), 这样的时间是可以接受的; 接着,再枚举 \(g^{s}\) 的指数 \(s,\) 若 \(s\) 与 \(\varphi(m)\) 互质, 则 \(g^{s} \bmod m\) 为一个原根。
具体步骤:
1.首先判断是否有原根
方法:这个数必须是2,4, \(p^{a}, 2 p^{a}\)
2.求第一个原根
方法:从1枚举到 \(m^{0.25},\) 设当前数为 \(i_{\circ}\) 如果 \((\mathrm{i}, \mathrm{m})=1\) 。
且对于 \(\varphi(m)\) 的所有质因子, 都有 \(i ^{\varphi(m) / prime} \bmod m\) 不等于1.
那么得到最小的原根\(g\)
3.求所有原根
方法:从1枚举到 \(\varphi(m)\),设当前数为i。若(i, \(\varphi(m))=1\) 。则 \(g^{i}\) 也是一个原根。
代码:
//P6091,给定n,d,求出有c个原根,输出c\n,ans[d],ans[2d]....ans[c/d*d]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
bool isPrime[maxn];
int Prime[maxn], primecnt = 0;
int phi[maxn];
void getPrime(int n){
memset(isPrime, 1, sizeof(isPrime));
isPrime[1] = 0;
phi[1] = 1;
for(int i = 2; i <= n; i++){
if(isPrime[i]){
Prime[++primecnt] = i;
phi[i]=i-1;
}
for(int j = 1; j <= primecnt && i*Prime[j] <= n; j++) {
isPrime[i*Prime[j]] = 0;
if(i % Prime[j] == 0){
phi[i*Prime[j]]=phi[i]*Prime[j];
break;
}
else{
phi[i*Prime[j]]=phi[i]*(Prime[j]-1);
}
}
}
}
ll fp(ll b,ll p,ll mod){
ll ans=1;
while(p){
if(p&1)ans=ans*b%mod;
b=b*b%mod;
p>>=1;
}
return ans;
}
bool check(int x){
for(int i=1;i<=primecnt;i++){
if(x%Prime[i]==0){
while(x%Prime[i]==0)x/=Prime[i];
if(x==2||x==1)return 1;
else return 0;
}
}
}
bool check2(int x,int n){
vector<int>pfacphi;
int temp=phi[n];
for(int i=1;i<=primecnt;i++){
if(temp%Prime[i]==0){
pfacphi.push_back(Prime[i]);
while(temp%Prime[i]==0)temp/=Prime[i];
}
if(temp<Prime[i]*Prime[i])break;
}
if(temp>1)pfacphi.push_back(temp);
for(auto b:pfacphi){
if(fp(x,phi[n]/b,n)==1){
return 0;
}
}
return 1;
}
int main () {
getPrime(maxn-5);
int T;
scanf("%d",&T);
while(T--){
int n,d;
scanf("%d%d",&n,&d);
if(!isPrime[n] && n!=2 && n!=4 && !check(n) && (n%2!=0 || !check(n/2))){
puts("0\n");continue;
}
int g;
for(int i=1;i<=n;i++){
if(__gcd(i,n)!=1)continue;
if(check2(i,n)){
g=i;break;
}
}
vector<int>ans;
ll temp=1;
for(int i=1;i<=phi[n];i++){
temp=temp*g%n;
if(__gcd(i,phi[n])==1)ans.push_back(temp);
}
sort(ans.begin(),ans.end());
printf("%d\n",ans.size());
for(int i=d-1;i<ans.size();i+=d){
if(i>d-1)printf(" ");
printf("%d",ans[i]);
}
puts("");
}
}
求单个原根
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e2+5;
ll m;
ll fac[maxn], cnt;
ll quickPower(ll a, ll b, ll M) {
ll ans = 1ll;
ll base = a;
while (b) {
if (b & 1) {
ans *= base;
ans %= M;
}
base *= base;
base %= M;
b >>= 1;
}
return ans;
}
void get_fac(ll x) {
x--;
ll m = sqrt(x) + 0.5;
for (int i = 2; i <= m; i++) {
if (x % i == 0) fac[cnt++] = i;
while (x % i == 0) x /= i;
}
if (x > 1) fac[cnt++] = x;
}
int main() {
scanf("%lld", &m);
get_fac(m);
for (ll g = 2; g < m; g++) {
int f = 1;
for (int j = 0; j < cnt; j++) {
if (quickPower(g, (m - 1) / fac[j], m) == 1ll) {
f = 0;
break;
}
}
if (f) {
printf("%d", g);
break;
}
}
}