欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【学习笔记】原根与指标

程序员文章站 2022-05-09 13:14:43
...

1. 阶

1.1 定义

  • 设正整数 n>1n>1aa 是满足 ana\perp naann 互质)的整数,则必有一个正整数 r[1,n]r\in [1,n],使得 ar1(mod n)a^r\equiv 1(mod~n)
  • 满足条件的最小的正整数 rr,称为 aann,记作 Ordn(a)Ord_n(a)
  • 注意,若 (a,n)1(a,n)\neq 1,则不存在 aann 的阶。

1.2 阶的性质

  1. ana\perp naN1(mod n)Ordn(a)Na^N\equiv 1(mod~n) \Leftrightarrow Ord_n(a)|N
  2. ana\perp nOrdn(a)φ(n)Ord_n(a)|\varphi(n)

2. 原根

2.1 定义

  • 设正整数 n>1n>1aa 是满足 ana\perp n 的整数,若 Ordn(a)=φ(n)Ord_n(a)=\varphi(n),则称 aa 为模 nn 的一个原根

2.2 解性

  • 设正整数 n>1n>1nn 的原根是一些关于 nn 的剩余类(原根可以不止一个)。
  • 所以研究原根时,我们只需要研究 [1,n)[1,n) 范围内的解即可。

2.3 原根的性质

  1. 只有 2,4,pk,2pk(p)2,4,p^k,2p^k(p是奇素数) 有原根。
  2. 一个数 nn 若存在原根,则它有 φ(φ(n))\varphi(\varphi(n)) 个原根。
  3. δ=Ordn(a)\delta=Ord_n(a),则 a0,a1,,aδ1a^0,a^1,\dots,a^{\delta-1}nn 两两不同余,当 aann 的原根时,a0,a1,,aδ1a^0,a^1,\dots,a^{\delta-1} 构成模 nn 的简化剩余系。特别地,当 nn 是质数时,a0,a1,,aδ1a^0,a^1,\dots,a^{\delta-1}nn 取模后对应为 {1,2,,n1}\{1,2,\dots,n-1\}

2.4 求一个数原根的方法

  • 我们已知一个数 nn 存在原根。
  • 我们现在要找到 nn 最小的原根。
  • 假设我们现在枚举到一个 gg,并且 (g,n)=1(g,n)=1,要验证 gg 是否是 nn 的原根。
  • 根据 Ordn(g)φ(n)Ord_n(g)|\varphi(n),我们只需要验证对于每个 dφ(n)(dφ(n))d|\varphi(n)(d\neq \varphi(n)),是否均满足 gd̸1(mod n)g^d\not\equiv 1(mod~n)
  • 我们设 φ(n)=i=1cpiki\varphi(n)=\prod_{i=1}^cp_i^{k_i}pip_i 是质数)。
  • 因为若 gd1(mod n)g^d\equiv 1(mod~n),则 gdk1(mod n)g^{dk}\equiv 1(mod~n),所以我们只需要验证,对于每个 ii,均满足 gnpi̸1(mod n)g^{\frac{n}{p_i}}\not\equiv1(mod~n) 即可。
  • 只需要暴力枚举验证即可。
  • 最小原根的范围都很小。
//mod是指需要求mod的最小原根,phi是mod的phi函数值
inline int find_root(const int &mod, const int &phi)
{
	static int p[MaxCnt]; 
	static int cnt; 
	cnt = 0; 
	 
	int x = phi; 
	for (int i = 2; i * i <= phi; ++i)
		if (x % i == 0)
		{
			p[++cnt] = i; 
			while (x % i == 0)
				x /= i; 
		}
	if (x > 1)
		p[++cnt] = x; 
	 
	for (int g = 1; g <= mod; ++g)
	{
		if (std::__gcd(g, mod) > 1) continue; 
		bool flg = true; 
		for (int i = 1; i <= cnt; ++i)
			if (qpow(g, phi / p[i], mod) == 1)
			{
				flg = false; 
				break; 
			}
		if (flg)
			return g; 
	}
	return -1; 
}

3. 指标

3.1 定义

  • ggnn 的一个原根,整数 ana \perp n,则在模 φ(n)\varphi(n) 意义下有唯一的整数 xx,使得 gxa(mod n)g^x\equiv a(mod~n),则称 xx 为以 gg 为底对模 nn 的一个指标,记作 x=indgax=ind_ga

3.2 指标的性质

  1. ab (mod p)indgaindgb (mod φ(p))a\equiv b~(mod~p)\Leftrightarrow ind_ga\equiv ind_gb~(mod~\varphi(p))
  2. indg(ab)ind(a)+ind(b) (mod φ(p))ind_g(ab)\equiv ind(a)+ind(b)~(mod~\varphi(p))
  3. indg(an)n×ind(a) (mod φ(p))ind_g(a^n)\equiv n\times ind(a)~(mod~\varphi(p))

3.3 求指标的方法

  • 求指标的运算也叫做离散对数
  • 求指标即求 gxa(mod p)g^x\equiv a(mod~p) 的最小自然数解。
  • 使用 BSGSBSGS 算法即可。

4. N次剩余问题

  • 问题:求解 xNa(mod p)x^N\equiv a(mod~p) 在模 pp 意义下的所有解,pp 是质数。
  • 首先我们知道,当 a0(mod p)a\equiv 0(mod~p) 时,方程只有 x=0x=0 一个解。
  • 否则 x[1,p)x\in[1,p)
  • 我们先找到 pp 的最小原根 gg
  • 因为 pp 是质数且 pap\nmid a,所以每个 x[1,p)x\in[1,p),我们都能找到 y=indgxy=ind_gxt=indgat=ind_ga
  • 那么原方程等价于 yNt(mod (p1))yN\equiv t(mod~(p-1))
  • 那么我们只需要用 exgcdexgcd 解这个简单的同余方程即可。
  • (N,p1)t(N,p-1)\nmid t 时,原方程无解。
  • 否则我们求出最小自然数解 y0y_0,然后将在 [1,p)[1,p) 内的 y=y0+kp1(N,p1)(kZ)y=y_0+k·\frac{p-1}{(N,p-1)}(k\in \mathbb Z)yy 记录下来。
  • 然后所有的 gyg^y 即为答案。
  • 代码:(BZOJ1319
#include <bits/stdc++.h>

int p, K, a, g, t; 
int anscnt, ans[1000010]; 

inline int qpow(int a, int b, const int &p)
{
	int res = 1; 
	for (; b; b >>= 1, a = 1LL * a * a % p)
		if (b & 1)
			res = 1LL * res * a % p; 
	return res; 
}

inline int find_root(const int &p)
{
	int n = p - 1, x = p - 1; 
	static int div[1000010], cnt; 
	
	cnt = 0; 
	for (int i = 2; i * i <= n; ++i)
		if (x % i == 0)
		{
			div[++cnt] = i; 
			while (x % i == 0)
				x /= i; 
		}
	if (x > 1)
		div[++cnt] = x; 
	for (int g = 1; g <= p; ++g)
	{
		bool flg = true; 
		for (int i = 1; i <= cnt; ++i)
			if (qpow(g, (p - 1) / div[i], p) == 1)
			{
				flg = false; 
				break; 
			}
		if (flg)
			return g; 
	}
	return -1; 
}

inline int solve_BSGS(const int &a, const int &b, const int &p)
{
	std::map<int, int> ha; 
	ha.clear(); 
	
	int t = ceil(sqrt(p)), x = 1; 
	for (int i = 1; i <= t; ++i)
	{
		x = 1LL * x * a % p; 
		ha[1LL * x * b % p] = i; 
	}
	int y = x; 
	for (int i = 1; i <= t; ++i)
	{
		if (ha.find(y) != ha.end())
			return i * t - ha[y]; 
		y = 1LL * y * x % p; 
	}
	return -1; 
}

inline int exgcd(const int &a, const int &b, int &x, int &y)
{
	if (!b)
		return x = 1, y = 0, a; 
	int res = exgcd(b, a % b, y, x); 
	y -= a / b * x; 
	return res; 
}

int main()
{
	scanf("%d%d%d", &p, &K, &a); 
	if (a == 0)
		return puts("1\n0"), 0; 
	
	g = find_root(p); 
	t = solve_BSGS(g, a, p); 
	
	int x, y; 
	int d = exgcd(p - 1, K, x, y); 
	
	if (t % d)
		return puts("0"), 0; 
	
	int mod = (p - 1) / d; 
	y = (1LL * y * (t / d) % mod + mod) % mod; 
	
	for (; y < p; y += mod)
		ans[++anscnt] = qpow(g, y, p); 
	
	std::sort(ans + 1, ans + anscnt + 1); 
	printf("%d\n", anscnt); 
	for (int i = 1; i <= anscnt; ++i)
		printf("%d\n", ans[i]); 
	
	return 0; 
}
相关标签: 数论