素数求原根

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<=m1,(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 ad1的最小正整数 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)=m1=piapjb...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)/pi1(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 ad1(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 ad1(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 ad1(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 ad1(modm),因此可以推导出 a φ ( m ) / p i ≢ 1 ( m o d m ) a^{\varphi(m) / p_i} \not\equiv 1 \pmod m aφ(m)/pi1(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)/pi1(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代码实现


#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;
			}
		}
	}
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值