GESP2025年3月认证C++五级( 第三部分编程题(2、原根判断))


🏰《魔法钥匙的考验》


一、🎮 故事背景

在一个神秘王国里,有一扇"超级宝库大门"🔐

  • 门的编号是一个质数 p

  • 小勇士拿着一把钥匙 a

👉 现在要判断:

🔑 a 能不能成为这扇门的"万能钥匙"(原根)?


二、🎯 什么是"原根"?

(1)👉 如果 a 是原根,那么:

复制代码
a^1 mod p
a^2 mod p
a^3 mod p
...
a^(p-1) mod p

👉 会把所有 1~p-1 全部走一遍(不重复!)


(2)🧠 直观理解

就像:

🎡 一个转盘,有 p-1 个位置

👉 每次乘 a,相当于"转一步"

如果:

✔ 能走遍所有位置 → 原根

❌ 有位置没走到 → 不是


(3)💡词条详细解释

原根是数论中的一个重要概念,尤其在模运算和密码学中有广泛应用。以下是关于原根的详细解释:

定义

设 m 是一个正整数,a 是一个与 m 互质的整数(即 gcd(a,m)=1)。如果 a 的幂次模 m 的结果能够生成所有与 m 互质的数,则称 a 为模 m 的原根。具体来说:

  • 集合 {a0,a1,a2,...,aϕ(m)−1} 模 m 的余数恰好覆盖所有与 m 互质的数(即模 m 的简化剩余系)。
  • 这里 ϕ(m) 是欧拉函数,表示小于 m 且与 m 互质的正整数的个数。

关键性质

  1. 存在性:并非所有正整数 m 都有原根。原根存在的充分必要条件是:

    • m=2,4;
    • m=pk(其中 p 是奇素数,k≥1);
    • m=2pk(其中 p 是奇素数,k≥1)。
  2. 阶数 :原根的阶(即最小的正整数 k 使得 ak≡1modm)等于 ϕ(m)。因此,原根是模 m 的本原根

  3. 数量:若模 m 存在原根,则原根的个数为 ϕ(ϕ(m))。例如:

    • 模 7 的原根有 2 个(3 和 5),因为 ϕ(6)=2。
    • 模 11 的原根有 4 个(2,6,7,8),因为 ϕ(10)=4。

例子

  1. 模 7 的原根
    • ϕ(7)=6,与 7 互质的数为 {1,2,3,4,5,6}。

    • 计算 3 的幂次模 7:

3^0≡1,3^1≡3,3^2≡2,3^3≡6,3^4≡4,3^5≡5mod7

结果覆盖所有简化剩余系,因此 3 是模 7 的原根。同理, 5 也是原根。


  1. 模 8 的原根
  • ϕ(8)=4,与 8 互质的数为 {1,3,5,7}。

  • 检查 3 的幂次:

3^0≡1,3^1≡3,3^2≡1mod8

无法覆盖所有简化剩余系,因此 3 不是原根。实际上,模 8 没有原根(因为 8 不满足存在原根的条件)。


应用

  1. 密码学:原根用于生成大素数模下的离散对数问题,是Diffie-Hellman密钥交换和ElGamal加密等协议的基础。
  2. 数论:原根与指数、指标等概念密切相关,用于解决同余方程和模运算问题。
  3. 随机数生成:原根的周期性可用于构造伪随机数序列。

如何寻找原根

寻找原根通常需要试算。对于模 p(素数),步骤如下:

  1. 计算 ϕ(p)=p−1,并分解 p−1 为质因数乘积:p−1=q1e1​​q2e2​​⋯qkek​​。

  2. 对每个候选数 a,检查是否满足:

a(p−1)/qi​≡1modp对所有i=1,2,...,k

若满足,则 a 是原根。

总结

原根是模运算中能够生成所有简化剩余系的"生成元",其存在性受模数形式的限制。它在密码学和数论中扮演核心角色,理解原根有助于深入掌握模运算和离散对数问题。


三、🧩 关键数学结论

我们不用真的去枚举所有幂(太慢❌)


1、💡 判定条件(核心🔥)

(1)设:

复制代码
φ(p) = p - 1

(2)把 p-1 分解质因数:

复制代码
p-1 = q1 × q2 × q3 × ...

(3)👉 只要检查:

复制代码
a^( (p-1)/qi ) mod p ≠ 1   (对所有质因子 qi)

(4)👉 全部成立 → ✔ 原根

👉 有一个成立为 1 → ❌ 不是


2、🧠 为什么?

👉 如果某次变成 1

说明:它"提前绕回来了",没有走完整圈


四、🪜 完整解题步骤


✅ Step 1:读入 a 和 p


✅ Step 2:计算 φ(p)

复制代码
φ(p) = p - 1

✅ Step 3:分解 φ(p)

👉 找出所有"质因子"

例如:

复制代码
p-1 = 12 = 2 × 2 × 3
→ 质因子:2 和 3

✅ Step 4:逐个检查

对每个质因子 q:

复制代码
检查:a^( (p-1)/q ) mod p

👉 如果出现:

复制代码
= 1

👉 直接判 ❌


✅ Step 5:全部通过

👉 输出:

复制代码
Yes

复制代码
No

五、⚡使用快速幂

(1)因为指数很大!

我们用:

👉 快速幂(递归或循环都行)


(2)💻 快速幂模板

复制代码
long long fastPow(long long a, long long b, long long mod) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

六、🧪 参考代码

复制代码
#include <bits/stdc++.h>
using namespace std;

// 快速幂
long long fastPow(long long a, long long b, long long mod) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main() {
    int T;
    cin >> T;

    while (T--) {
        long long a, p;
        cin >> a >> p;

        long long phi = p - 1;
        long long temp = phi;

        vector<long long> factors;

        // 分解质因子
        for (long long i = 2; i * i <= temp; i++) {
            if (temp % i == 0) {
                factors.push_back(i);
                while (temp % i == 0) temp /= i;
            }
        }
        if (temp > 1) factors.push_back(temp);

        bool ok = true;

        // 检查原根条件
        for (auto q : factors) {
            if (fastPow(a, phi / q, p) == 1) {
                ok = false;
                break;
            }
        }

        if (ok) cout << "Yes\n";
        else cout << "No\n";
    }

    return 0;
}

七、🎯 知识点总结:


🧠 ① 模型转化能力(最关键🔥)

👉 "看起来复杂的定义"

👉 → 转化成"几个幂运算判断"


🧠 ② 降维简化思维

👉 原本要检查 p-1 次

👉 现在只需要检查"质因子个数次"


🧠 ③ 快速幂(核心工具)

👉 所有:

  • 模运算

  • 指数很大

都可以用它!


相关推荐
winner88812 小时前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
数据皮皮侠2 小时前
上市公司创新韧性数据(2000-2024)|顶刊同款 EIR 指数
大数据·人工智能·算法·智慧城市·制造
WL_Aurora2 小时前
Python 算法基础篇之链表
python·算法·链表
科研前沿2 小时前
纯视觉无感解算 + 动态数字孪生:室内外无感定位技术全新升级
大数据·人工智能·算法·重构·空间计算
AI进化营-智能译站2 小时前
ROS2 C++开发系列07-高效构建机器人决策逻辑,运算符与控制流实战
开发语言·c++·ai·机器人
winner88812 小时前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++
不会编程的懒洋洋2 小时前
C# P/Invoke 基础
开发语言·c++·笔记·安全·机器学习·c#·p/invoke
Wadli3 小时前
26.单调栈
算法
晨曦夜月3 小时前
进程的五大状态及特殊进程解析
linux·服务器·算法