浅谈阶与原根

阶的定义:若a,p均为正整数,且gcd(a,p)=1,称满足a^{r}\equiv 1(mod p)的最小正整数r为a模p的阶,记作ord_{p}a.

定义枯燥乏味,举个栗子,如a=3,p=7,根据暴力递推我们可以得到这样一张有向有环图

 注意:这不是特例,再举个栗子,如a=2,p=7,可以得到

 其中节点的编号即为出现的模数

根据欧拉定理可知,当gcd(a,p)=1,a>0,p>0,p为素数时一定有a^{\phi (p)}\equiv 1(mod p),再根据阶的定义,一定有阶r | \phi (p),这说明在这张图上走\phi (p)步一定会回到原点,但没有保证的是这\phi (p)步一定是把这个环走了一遍,也可能走了两遍...n遍,所以这个环的长度一定是\phi (p)的因子,那求阶可能想到的一种做法就是去枚举\phi (p)的所有因子,看最小的a的幂次模p等于1是什么时候,但这并不是最好的做法。

假设阶是d,a^{x}\equiv 1(mod p),那么d | x,又知道欧拉定理是a^{\phi (p)}\equiv 1(mod p), 根据唯一分解定理分解\phi (p)=p_{1}^{e_{1}}\cdot p{_{2}}^{e_{2}}\cdot \cdot \cdot p_{n}^{e_{n}},先令d=\phi (p),枚举\phi (p)的每个质因子p_{i},当d%p_{i}==0并且a^{d/p_{i}}%p\equiv 1时,d/=p_{i},否则什么都不做,这说明的是此时的p_{i}对于d来说相当于对着环走了p_{i}次,最后虽然也是回到了原点,但并不是环的长度,所以要除掉,如果d/p_{i}后取模不是1了说明回不到原点,因此这个因子是关键的,不能除掉。综上所述,假设环的长度为L,除掉的相当于n*L中的n的因子,这里力求的是L的长度,所以要使得n为1,因此枚举所有的\phi (p)的因子。

还有一个前置小芝士:\phi (p)为1到p-1中与p互质的正整数的个数,显而易见素数的欧拉函数的值为p-1

求阶代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int a, p;//底数和模数
    cin >> a >> p;
    function<LL(LL, LL)> qp = [&](LL a, LL b) {
        LL res = 1;
        for (; b; b >>= 1, a = a * a % p) {
            if (b & 1) {
                res = res * a % p;
            }
        }
        return res % p;
    };
    int d = p - 1, m = p - 1;
    vector<int> z;
    for (int i = 2; i * i <= m; i++) {
        if (m % i == 0) {
            z.push_back(i);
            while (m % i == 0) {
                m /= i;
            }
        }
    }
    if (m != 1) {//如果还剩下一个没取到的素数还要把它放进因子集合里
        z.push_back(m);
    }
    int len = z.size();
    for (int i = 0; i < len; i++) {
        while (d % z[i] == 0 and qp(a, d / z[i]) == 1) {
            d /= z[i];
        }
    }
    cout << d << '\n';//阶
    return 0;
}

原根

原根的定义:如果g(mod m)的阶为\phi (m),那么g就是m的原根。

原根就是特殊情况的阶。

g^{0},g^{1},...,g^{\phi (m)-1}构成了模m的简化剩余系。

只有1,2,4,p^{a},2p^{a}存在原根(p为奇素数 (奇素数:除2以外的素数) )。

注意:2的幂次可能是没有原根的

结论1:原根很多

结论2:最小的原根不会很大且2到p-1之间随机分布

如何判断一个数g是否是原根?

类比于求阶的计算,设模数m,枚举\phi (m)的所有因子p_{i},如果所有g^{\phi (m)/p_{i}}\not\equiv 1(mod m),那么g就是原根,否则不是原根,即阶可以变得更小,所以阶不等于\phi (m)

注意:如果a是p的原根,a不一定是p^{2}的原根,如果a是p^{2}的原根,那么一定是更高次的原根

如果g是m的原根,那么循环节里一定会出现1到m之间与m互质的数,都可以表示成g^{i}其中(i>=0,i<\phi (m)) (简化剩余系)

根据上述的结论2可知,求解原根的时候可以从1开始暴力往上计算。

求解原根代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int p;//求解素数p的原根
    cin >> p;
    function<LL(LL, LL)> qp = [&](LL a, LL b) {
        LL res = 1;
        for (; b; b >>= 1, a = a * a % p) {
            if (b & 1) {
                res = res * a % p;
            }
        }
        return res % p;
    };
    vector<int> z;//因子集合
    int m = p - 1;
    for (int i = 2; i * i <= m; i++) {
        if (m % i == 0) {
            z.push_back(i);
            while (m % i == 0) {
                m /= i;
            }
        }
    }
    if (m != 1) {//还有素数没有除完
        z.push_back(m);
    }
    int len = z.size();
    for (int g = 1; g < p; g++) {
        bool ok = true;
        for (int i = 0; i < len; i++) {
            if (qp(g, (p - 1) / z[i]) == 1) {//阶可以更小,因此不是原根
                ok = false;
                break;
            }
        }
        if (ok) {
            cout << g << '\n';
            exit(0);
        }
    }
    //否则没有原根
    return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值