原根

原根

阶:(a, m in mathbb{N}^{+},)(a perp m,) 使 (a^{x} equiv 1(mod 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}} ot equiv 1(mod m))

求所有原根

定理:(g) 为模 (m) 的原根, 则集合 (S=left{g^{s} mid 1 leq s leq varphi(m), s perp varphi(m) ight}) 是模 (m) 的所有原根。

可以证明, 最小原根是不大于 (sqrt[4]{m}) 级别的。因此, 不妨枚举 ([1, sqrt[4]{m}]) 的整数, 得到最小原根 (g), 这样的时间是可以接受的; 接着,再枚举 (g^{s}) 的指数 (s,)(s)(varphi(m)) 互质, 则 (g^{s} mod 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} mod m) 不等于1.
那么得到最小的原根(g)
3.求所有原根
方法:从1枚举到 (varphi(m)),设当前数为i。若(i, (varphi(m))=1) 。则 (g^{i}) 也是一个原根。

代码:

//P6091,给定n,d,求出有c个原根,输出c
,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
");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
",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;
        }
    }
}
原文地址:https://www.cnblogs.com/ucprer/p/14407050.html