素数求原根

1 模m原根的定义

1.1符号说明:

Z m ∗ Z_m^* Zm∗:代表满足 1 < = i < = m − 1 , ( i , m ) = 1 1<=i<=m-1,(i,m)=1 1<=i<=m−1,(i,m)=1的数字 i i i组成的集合
o r d m ( a ) ord_m(a) ordm(a):代表 a ( m o d m ) a(mod m) a(modm)在 Z m ∗ Z_m^* Zm∗中的阶,即使得 a d ≡ 1 a^d \equiv 1 ad≡1的最小正整数 d d d。
φ ( m ) \varphi(m) φ(m):数 m m m简化剩余系中的元素个数,即集合 Z m ∗ Z_m^* Zm∗中的元素个数。

1.2定义

若 o r d m ( a ) = φ ( m ) ord_m(a)= \varphi(m) ordm(a)=φ(m),则称 a a a为模 m m m的原根。即在 Z m ∗ Z_m^* Zm∗的乘法上的生成元。

2 素数求原根

2.1 理论基础

φ ( m ) = m − 1 = p i a ∗ p j b . . . p k c ( p i , p j . . . p k \varphi(m)=m-1=p_i^a*p_j^b...p_k^c(p_i,p_j...p_k φ(m)=m−1=pia∗pjb...pkc(pi,pj...pk为素数 ) ) )。对于任意一个 p i p_i pi,都满足 a φ ( m ) / p i ≢ 1 ( m o d m ) a^{\varphi(m)/p_i} \not\equiv 1 \pmod m aφ(m)/pi≡1(modm),则 a a a为模 m m m的原根。
证明: 根据拉个格朗日定理,对于任意一个 a a a ∈ \in ∈ Z m ∗ Z_m^* Zm∗,有 o r d m ( a ) ord_m(a) ordm(a) | φ ( m ) \varphi(m) φ(m),即当且仅当d| φ ( m ) \varphi(m) φ(m)时,才会使得 a d ≡ 1 ( m o d m ) a^d \equiv 1 \pmod m ad≡1(modm)。

根据模m原根的定义,要求 o r d m ( a ) = φ ( m ) ord_m(a)= \varphi(m) ordm(a)=φ(m),即当且仅当 d = φ ( m ) d=\varphi(m) d=φ(m)时,才有 a d ≡ 1 ( m o d m ) a^d \equiv 1 \pmod m ad≡1(modm)。

进一步地推导可知, d ≠ φ ( m ) d\not =\varphi(m) d=φ(m)且d| φ ( m ) \varphi(m) φ(m)时,若对于所有d都满足 a d ≢ 1 ( m o d m ) a^d \not\equiv 1 \pmod m ad≡1(modm),则a为原根 (条件一)

对于任意一个 d d d,因为 d ≠ φ ( m ) d\not =\varphi(m) d=φ(m)且d| φ ( m ) \varphi(m) φ(m),因此存在i,使得 d ∣ d | d∣ φ ( m ) / p i \varphi(m) / p_i φ(m)/pi,由于 a d ≢ 1 ( m o d m ) a^d \not\equiv 1 \pmod m ad≡1(modm),因此可以推导出 a φ ( m ) / p i ≢ 1 ( m o d m ) a^{\varphi(m) / p_i} \not\equiv 1 \pmod m aφ(m)/pi≡1(modm)。

因此,若对于所有 p i p_i pi,满足 a φ ( m ) / p i ≢ 1 ( m o d m ) a^{\varphi(m) / p_i} \not\equiv 1 \pmod m aφ(m)/pi≡1(modm),则a为原根 (条件二)

拉格朗日定理:在有限群 < G , ∗ > <G,*> <G,∗>中,每个元素的周期是# G G G(集合G的元素的个数)的因子。

2.2 算法流程

算法的整体思路是根据2.1节所述的条件二来制定的。其具体的流程如下:
step1: 使用素数线性筛法,找出 1 1 1到 m m m中的所有素数。
step2: 确定m的质因子集合{ p i p_i pi}。
step3: 遍历 2 2 2到 m m m中的所有数,假设当前处理的数为a。遍历m的所有质因子,若都满足 a φ ( m ) / p i a^{ \varphi(m) / p_i} aφ(m)/pi ≢ \not\equiv ≡ 1 ( m o d m ) \pmod m (modm),则a为模m的原根。

2.3代码实现

cpp 复制代码
#include<iostream>
using namespace std;
#define NUM 998244380
bool is_prime[NUM] = { 0 };
#define NMAX 400000
long long prime[NMAX];
long long prime_num = 0;
long long m;//待处理的素数
long long p_factor[NMAX];
long long p_factor_num = 0;
void get_prime(long long n) {
	for (long long m = 2; m < n; m++) {
		if (m == 98244379)
		{
			cout << m << endl;
		}
		if (m == 298244379)
		{
			cout << m << endl;
		}
		//数m没有被筛选过
		if (!is_prime[m]) {
			if (prime_num > NMAX) {
				cout << "越界" << endl;
			}
			prime[prime_num++] = m;
		}
		//筛选掉以prime[i]为最小素因子的合数m*prime[i]
		for (long long i = 0; i < prime_num; i++) {
			if ((m * prime[i]) < n) is_prime[m * prime[i]] = 1;
			if (m % prime[i] == 0) break;//m*prime[j](j>i)的最小素因子为m的最小素因子prime[i]而非prime[j],因此跳出循环
		}
	}
	//打印输出
	/*for (int i = 0; i < prime_num; i++) {
		cout << prime[i] << " ";
	}
	cout << endl;*/
}
void get_p_factor(long long m) {
	p_factor_num = 0;
	for (long long i = 0; i < prime_num; i++) {
		if ((m - 1) % prime[i] == 0) p_factor[p_factor_num++] = prime[i];
	}
}
long long ksm(long long a, long long d, long long mod) {
	long long ret = 1;
	while (d) {
		if ((d & 1) == 1) ret = (ret * a) % mod;//按位与为&,而&&代表的是条件与
		a = (a * a) % mod;
		d >>= 1;
	}
	return ret;
}
int judge(long long a) {
	for (long long i = 0; i < p_factor_num; i++) {
		if (ksm(a, (m - 1) / p_factor[i], m) == 1) {
			return 0;
		}
	}
	return 1;
}
int main() {
	//1.获取所有的素数
	get_prime(1000);
	while (cin >> m) {
		//获取m的所有质因子
		get_p_factor(m);
		//遍历2...m-1进行判断
		for (long long a = 2; a < m; a++) {
			if (judge(a)) {
				cout << a << endl;
				break;
			}
		}
	}
}
相关推荐
shymoy37 分钟前
Radix Sorts
数据结构·算法·排序算法
风影小子1 小时前
注册登录学生管理系统小项目
算法
黑龙江亿林等保1 小时前
深入探索哈尔滨二级等保下的负载均衡SLB及其核心算法
运维·算法·负载均衡
lucy153027510791 小时前
【青牛科技】GC5931:工业风扇驱动芯片的卓越替代者
人工智能·科技·单片机·嵌入式硬件·算法·机器学习
杜杜的man1 小时前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang
小沈熬夜秃头中୧⍤⃝1 小时前
【贪心算法】No.1---贪心算法(1)
算法·贪心算法
木向2 小时前
leetcode92:反转链表||
数据结构·c++·算法·leetcode·链表
阿阿越2 小时前
算法每日练 -- 双指针篇(持续更新中)
数据结构·c++·算法
skaiuijing2 小时前
Sparrow系列拓展篇:对调度层进行抽象并引入IPC机制信号量
c语言·算法·操作系统·调度算法·操作系统内核
Star Patrick3 小时前
算法训练(leetcode)二刷第十九天 | *39. 组合总和、*40. 组合总和 II、*131. 分割回文串
python·算法·leetcode