原根

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-31 21:11   11   0

定义

数论,特别是整除理论中,原根是一个很重要的概念。

对于两个正整数 (a,m)=1, 由欧拉定理可知,存在正整数d\leq m-1, 比如说欧拉函数d=\varphi (m),即小于等于m的正整数中与m互素的正整数的个数,使得 a^{d}\equiv 1{\pmod  {m}}

由此,在(a,m)=1时,定义a对模m的指数 \delta m(a) 为使 a^{d}\equiv 1{\pmod  {m}} 成立的最小的正整数 d 。由前知 \delta m(a) 一定小于等于 \varphi (m) ,若 \delta m(a) = \varphi (m) ,则称 a 是模 m 的原根。

例子

m=7 ,则 \varphi (m) 等于6。

  • a=2 ,由于 2^{3}=8\equiv 1{\pmod  {7}} ,而 \displaystyle 3<6 ,所以 2 不是模 7 的一个原根。
  • a=3 ,由于 3^{1}\equiv 3{\pmod  {7}}3^{2}\equiv 2{\pmod  {7}}3^{3}\equiv 6{\pmod  {7}}3^{4}\equiv 4{\pmod  {7}}3^{5}\equiv 5{\pmod  {7}}3^{6}\equiv 1{\pmod  {7}} ,因此有 Ord_{7}(3)=6=\varphi (7) ,所以 3 是模 7 的一个原根。

性质

  • 可以证明,如果正整数 (a,m)=1 和正整数 d 满足 a^{d}\equiv 1{\pmod  {m}},则 Ord_{m}(a) 整除 d。因此 Ord_{m}(a) 整除 \varphi (m) 。在例子中,当 a=3 时,我们仅需要验证 3 的 2、3 次方模 7 的余数即可,如果其中有一个是1,则3就不是原根。
  • \delta =Ord_{m}(a) ,则 a^{0},a^{1},a^{2}\cdots ,a^{{\delta -1}} 模 m 两两不同余。因此当a是模{\displaystyle m}m的原根时, a^{0},a^{1},a^{2}\cdots ,a^{{\delta -1}} 构成模 m 的简化剩余系
  • m 有原根的充要条件是 m=2,4,p^{n},2p^{n} ,其中 p 是奇素数, n 是任意正整数
  • 对正整数 (a,m)=1 ,如果 a 是模 m 的原根,那么 a 是整数模m乘法群(即加法群 Z/mZ 的可逆元,也就是所有与 m 互素的正整数构成的等价类构成的乘法群)Zm×的一个生成元。由于Zm×有 \varphi (m) 个元素,而它的生成元的个数就是它的可逆元个数,即 \varphi (\varphi (m)) 个,因此当模 m 有原根时,它有 \varphi (\varphi (m)) 个原根。

一些数的原根列表

m模m的原根(有*号的数没有原根,此时是有最大模m周期的数)周期 (OEISA002322)
101
211
322
432
52, 3, 44
652
73, 56
8*3, 5, 72
92, 56
103, 74
112, 6, 7, 810
12*5, 7, 112
132, 6, 7, 1112
143, 56
15*2, 7, 8, 134
16*3, 5, 11, 134
173, 5, 6, 7, 10, 11, 12, 1416
185, 116
192, 3, 10, 13, 14, 1518
20*3, 7, 13, 174
21*2, 5, 10, 11, 17, 196
227, 13, 17, 1910
235, 7, 10, 11, 14, 15, 17, 19, 20, 2122
24*5, 7, 11, 13, 17, 19, 232
252, 3, 8, 12, 13, 17, 22, 2320
267, 11, 15, 1912
272, 5, 11, 14, 20, 2318
28*3, 5, 11, 17, 19, 236
292, 3, 8, 10, 11, 14, 15, 18, 19, 21, 26, 2728
30*7, 13, 17, 234
313, 11, 12, 13, 17, 21, 22, 2430
32*3, 5, 11, 13, 19, 21, 27, 298
33*2, 5, 7, 8, 13, 14, 17, 19, 20, 26, 28, 2910
343, 5, 7, 11, 23, 27, 29, 3116
35*2, 3, 12, 17, 18, 23, 32, 3312
36*5, 7, 11, 23, 29, 316

除了直接运算以外,至今还没有一个办法可以找到模特定m时的原根,但假如已知模m有一个原根,则可找出它其他的原根。

题目一:求最小的原根

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int Maxn = 1e6+10;
const int N = 1e8+10;
int prime[Maxn];//存储素数
int sprime[Maxn];//存储P-1的素因子
bool check[Maxn];
bitset<Maxn>pri;//结果只有0和1,判断是否为素数
int k;//记录Maxn以内的素数个数
int cnt;//记录素因子的个数
void is_prime(){
    pri.set();//将所有的二进制数都标为1
    for(int i=2; i<Maxn; i++) {
        if(pri[i]) {
            prime[k++]=i;
            for(int j=i+i; j<Maxn; j+=i)
                pri[j]=0;
        }
    }
}
void Divide(int n)//将n分解为素因子
{
    cnt=0;
    int t=(int)sqrt(1.0*n);
    for(int i=0; prime[i]<=t; i++) {
        if(n%prime[i]==0) {
            sprime[cnt++]=prime[i];
            while(n%prime[i]==0)//因为有可能有多个peime[i]
                n/=prime[i];
        }
    }
    if(n>1)
        sprime[cnt++]=n;//可能只有自己一个素因子
}
LL modexp(LL a,LL b,int mod)//快速幂取余
{
    LL res=1;
    while(b>0) {
        a=a%mod;
        if(b&1)
            res=res*a%mod;
        b=b>>1;
        a=a*a%mod;
    }
    return res;
}

int main()
{
    int p;
    Is_prime();
    while(~scanf("%d",&p)) {
        Divide(p-1);
        for(int g=2; g<p; g++) {
            int flag=1;
            for(int i=0; i<cnt; i++) {
                int t=(p-1)/sprime[i];
                if(modexp(g,t,p)==1) {
                    flag=0;
                    break;
                }
            }
            if(flag) {
                int root=g;
                printf("%d\n",root);
                break;//去掉break的话是求所有的原根,加上break是求最小的原根、
            }
        }
    }
    return 0;
}

题目二:求原根的个数

#include<bits/stdc++.h>
using namespace std;
#define clr(a) memset(a,0,sizeof(a))

typedef long long ll;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;

ll n;
ll get(ll n){
    ll ans = n;
    for(int i=2;i*i<=n;i++){
        if(n %i == 0){
            ans = ans / i * (i - 1);
            while(n %i == 0)
                n /= i;
        }
    }
    if(n > 1)   ans = ans / n * (n - 1);
    return ans ;
}
int main(){
    while(scanf("%lld",&n)!=EOF){
        printf("%lld\n",get(get(n)));
    }
    return 0;
}

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP