阶与原根学习笔记

本博客参考于:https://www.cnblogs.com/cytus/p/9296661.html

  阶的定义:设 m > 1,且gcd(a,m) = 1,那么使得 ar ≡ 1(mod m) 成立最小的正整数 r 称为,a 对模 m 的阶,记作 δm(a)。

相关定理:
定理一:

  若 m > 1,并且gcd(a,m) = 1,又满足 an ≡ 1 (mod m),则 δm(a) | n。

定理二:

  由定理一可推得:δm(a) | ϕ(m)。
  证明:有欧拉定理得 aϕ(m) ≡ 1(mod m)可知,δm(a) <= ϕ(m),再由定理一即得证。

相关推论:
推论一:

  若p和q为奇素数,且q | (ap−1), 则要么有q | (a−1),要么有q = 2kp+1,其中 k 为某整数。

推论一:

  2p-1 的任何因子必取 2kp+1 的形式,其中 k 为某整数。

原根

  原根的定义:设 m 为正整数,a 为整数,如果 a 对模 m 的阶等于ϕ(m),那么称 a 为模 m 的一个原根。( aϕ(m) ≡ 1(mod m) )

相关定理:
定理一:

  一个正整数m有原根的充要条件是m = 2,4,pe,2pe,其中,p奇素数,e为正整数。

定理二:

  每一个素数 p 都有 ϕ(p−1) 个原根,事实上,每一个正整数 m 都有 ϕ(ϕ(m)) 个原根。

定理三:

  若g是m的一个原根,则g,g2,…… ,gϕ(m)各数对m取模的非负最小剩余就是小于m且与m互质的ϕ(m)个数的一个排列。

原根的求法
方法一:

质数的原根 = ϕ(p−1)。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 300;
int pri[maxn],tot;
bool v[maxn];
void primes()	//线性筛素数
{
    tot = 0;
    memset(v,true,sizeof(v));
    for(int i=2;i<maxn;i++){
        if(v[i])
            pri[++tot] = i;
        for(int j=1;j<=tot&&pri[j]*i<maxn;j++){
            v[i*pri[j]] = false;
            if(i%pri[j]==0) break;
        }
    }
}
int phi(int n)	//欧拉函数
{
    int ans = 1;
    for(int i=1;pri[i]*pri[i]<=n;i++){
        if(n%pri[i]==0){
            ans *= (pri[i]-1);
            n /= pri[i];
            while(n%pri[i]==0){
                ans *= pri[i];
                n /= pri[i];
            }
        }
    }
    if(n>1)ans *= (n-1);
    return ans;
}
int main(void)
{
    int n;
    primes();
    while(scanf("%d",&n)!=EOF){
        printf("%d\n",phi(n-1));
    }
    return 0;
}
方法二:

正整数的原根求解:ϕ(ϕ(m))

这里就演示一下打表……

#include<bits/stdc++.h>
using namespace std;

using namespace std;
const int N = 2000010;
const int M = 1000010;
const int inf = 2000010;
bool v[N],vv[N];	//v代表是否是素数,vv代表是否原根
int phi[N],prime[M],m;	//phi 欧拉函数,prime 素数表
void pri_r()
{
	m = 0;
	prime[++m]=2;v[4]=1;
	phi[2]=1;phi[4]=2;phi[1]=1;
	for(int i=3;i<=inf;i++){
		if(!v[i]){
			prime[++m] = i;
			phi[i] = i-1;
			vv[i] = 1;
		}
		for(int j=1;j<=m&&i*prime[j]<=inf;j++){
			v[i*prime[j]] = 1;
			if(i%prime[j]==0){
				vv[i*prime[j]]=vv[i];
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]] = phi[i]*(prime[j]-1);
		}
	}
	vv[2] = vv[4]= 1;
}
int main()
{
	pri_r();
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		if(vv[x]||(!(x&1)&&vv[x>>1]&&x!=8))	//
            printf("%d\n",phi[phi[x]]);
		else
            puts("0");	//没有原根
	}
	return 0;
}

判断一个数是不是m的原根:

方法一:

首先求ϕ(m)的素幂分解式:
  ϕ(m)=p1e1 ∗ p2e2 ∗ … ∗ pkek
然后枚举g,若恒满足
  gϕ(m)/pi ≠ 1(modm),其中i=1,2,…,k
则g是m的一个原根。

方法二:

  如果m是质数:
首先求ϕ(m-1)的素幂分解式:
  ϕ(m-1)=p1e1 ∗ p2e2 ∗ … ∗ pkek
然后枚举g,若恒满足
  g(m-1)/pi ≠ 1(modm),其中i=1,2,…,k
则g是m的一个原根。

例题:

http://www.51nod.com/Challenge/Problem.html#problemId=1135

1135 原根
设m是正整数,a是整数,若a模m的阶等于φ(m),则称a为模m的一个原根。(其中φ(m)表示m的欧拉函数)
给出1个质数P,找出P最小的原根。
输入样例
3
输出样例
2

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#define ll long long
#define ld long double
#define ull unsigned long long
using namespace std;
const int maxn = 1000010;
const ll lnf = 0x3f3f3f3f3f3f3f;
int pri[maxn],tot;
bool v[maxn];
int p[maxn],cnt;
void primes()
{
    tot = 0;
    memset(v,true,sizeof(v));
    for(int i=2;i<maxn;i++){
        if(v[i])
            pri[++tot] = i;
        for(int j=1;j<=tot&&pri[j]*i<maxn;j++){
            v[i*pri[j]] = false;
            if(i%pri[j]==0) break;
        }
    }
}
void phi(int n)	
{
    for(int i=1;pri[i]*pri[i]<=n;i++){
        if(n%pri[i]==0){
            p[++cnt] = pri[i];
            n /= pri[i];
            while(n%pri[i]==0){
                n /= pri[i];
            }
        }
    }
    if(n>1){
        p[++cnt] = n;
    }
}
int ksm(int a,int b,int c)
{
    int ans = 1;
    for(int i=b;i;i>>=1){
        if(i&1) ans = 1ll*ans*a % c;
        a = 1ll*a*a % c;
    }
    return ans%c;
}
bool judge(int g,int m)
{
    for(int i=1;i<=cnt;i++){
        int gm = ksm(g,(m-1)/p[i],m);
        if(gm==1)   return false;
    }
    return true;
}
int main(void)
{
    int n;
    primes();
    scanf("%d",&n);
    cnt = 0;phi(n-1);

    for(int i=2;i<n;i++){
        if(judge(i,n)){
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逃夭丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值