数论笔记-原根

原根

阶的定义与基本性质

定义aZ,mNgcd(a,m)=1 ,那么满足 ax1(modm) 的最小正整数 x 称为 am 的阶,记作 δm(a)ordm(a)

阶是群论的概念, am 的阶即 a 在模 m 缩系下生成的乘法群大小。

性质1 a,a2,,aδm(a)m 互不相同。

性质2 正整数 n 满足 an1(modm) ,当且仅当 δm(a)n

性质3 δm(a) 一定存在,且 δm(a)φ(m)

性质4a,bZ ,有 gcd(δm(a),δm(b))=1δm(ab)=δm(a)δm(b)

性质5kN ,有 δm(ak)=δm(a)gcd(δm(a),k)

性质1的证明:

假设存在不同的两个数 i,j ,满足 aiaj(modm) ,那么有 a|ij|1(modm) ,其中 |ij|<δm(a) ,与定义相悖,因此原命题成立。

性质2的证明:

必要性:

使用带余数除法, n 除以 δm(a)n=qδm(a)+r ,其中 r[0,δm(a))

那么, anaqδm(a)+rar1(modm) ,根据阶的定义,rδm(a)r=0

因此 r=0 ,即 δm(a)n

充分性:

显然。

性质3的证明:

根据欧拉定理,有 aφ(m)1(modm) ,又根据性质2,阶一定存在,且是 φ(m) 的因数。

性质4的证明:

必要性:

aδm(a)bδm(a)1(modm) ,可得 (ab)lcm(δm(a),δm(b))1(modm) ,因此 δm(ab)lcm(δm(a),δm(b)) 。又 δm(ab)=δm(a)δm(b) ,因此 δm(a)δm(b)lcm(δm(a),δm(b)) ,所以 gcd(δm(a),δm(b))=1

充分性:

(ab)δm(ab)1(modm) ,得 (ab)δm(ab)δm(b)1(modm) ,因此 aδm(ab)δm(b)1(modm) ,所以 δm(a)δm(ab)δ(b) ,又 gcd(δm(a),δm(b))=1 ,根据整除的基本性质4,有 δm(a)δm(ab)

同理有 δm(b)δm(ab)

又因为 gcd(δm(a),δm(b))=1 ,所以 δm(a)δm(b)δm(ab)

同时 (ab)δm(a)δm(b)1(modm) ,所以 δm(ab)δm(a)δm(b)

综上,根据整除的基本性质7, δm(ab)=δm(a)δm(b)

性质5的证明:

(ak)δm(a)1(modm) ,因此 δm(a)kδm(ak) ,所以 δm(a)gcd(δm(a),k)δm(ak)

同时 (ak)δm(a)gcd(δm(a),k)(aδm(a))kgcd(δm(a),k)1(modm) ,所以 δm(ak)δm(a)gcd(δm(a),k)

综上,根据整除的基本性质7,δm(ak)=δm(a)gcd(δm(a),k)

原根

原根的定义与基本性质

定义gZ,mNgcd(g,m)=1 ,若 δm(g)=φ(m) ,则称 g 为模 m 的原根。

原根即群论中的生成元。“根”表示方程 xφ(m)1(modm) 的一个解,“原”表示能生成模 m 缩系的所有元素。因此,模数 m 存在原根,也代表模 m 缩系的乘法群是一个循环群。

性质1m 存在原根 g ,那么 g,g2,,gφ(m) 构成模 m 的简化剩余系。

性质2m 存在原根,那么对于任意 φ(m) 的因子 d ,模 md 阶元素个数为 φ(d)

  • 推论1(性质2的推论,原根个数)m 存在原根,那么原根的个数为 φ(φ(m))

  • 推论2(性质2的推论)m 存在原根,那么对于任意 φ(m) 的因子 dxd1(modm) 的解恰好有 φ(d) 个。

性质3 素数 p 的最小原根 g ,满足 g=Ω(logp)=O(p0.25+ε),ε>0

性质4 若素数 p 的原根是 g ,那么 pg+p 中必有一个是 pα 的原根,gg+pα 中的奇数是 2pα 的原根。

性质1的证明:

由于 δm(g)=φ(m) ,因此 g,g2,,gφ(m) 互不相同且都与 m 互质,因此其构成模 m 的简化剩余系。

性质2的证明:

d=φ(m)d ,那么 δm(gd)=φ(m)gcd(d,φ(m))=d

设正整数 k 满足 kd ,根据阶的性质5,当且仅当 gcd(k,d)=1 时, δm(gkd)=δm(gd)=d ,这样的 kφ(d) 个。

而根据性质1, gkd 一定模 m 互不相同,所以模 md 阶的元素至少有 φ(d) 个。

k>d 时,kd(kmodd)d(modφ(m)) ,与 kd 时的情况重复。

综上模 md 阶元素个数为 φ(d)

推论1的证明:

根据性质2,模 mφ(m) 阶元素共有 φ(φ(m)) 个。

推论2的证明:

根据性质2,模 md 阶元素有 φ(d) 个。

同时,仅对于满足 dφ(d) 的正整数 dd 阶元素都是方程的解,显然这些元素各不相同。

因此,满足方程解的个数恰好为 dφ(d)φ(d)=φ(d) 个。

性质3的证明:

出自论文,详见 OI-wiki 。

性质4的证明:

不会qwq

原根判定性定理

定理1(原根判定性定理) g 是模 m 的原根,当且仅当对于任意 φ(m) 的素因数 p ,满足 gφ(m)p1(modm)

定理1的证明:

必要性:显然。

充分性:

假设 g 不是模 m 的原根,即存在 t<φ(m) 满足 gt1(modm)

根据裴蜀定理,存在一组整数 x,y 使得 xt=yφ(m)+gcd(t,φ(m))

此时,有 1gxtggcd(t,φ(m))(modm) ,而 gcd(t,φ(m))φ(m)gcd(t,φ(m))t<φ(m)

因此,存在 φ(m) 的素因数 p ,使得 gcd(t,φ(m))φ(m)p ,所以 gφ(m)p1(modm) ,矛盾。

因此 g 是模 m 的原根。

原根存在性定理

定理1(原根存在性定理) 模数 m 的原根存在,当且仅当 m=2,4,pα,2pα ,其中 p 为奇素数,αN

定理1的证明:

可参考 OI-wiki 。

原根的求法

枚举法(最小原根)

n 的最小原根大致分为以下几步:

  1. n 质因数分解,根据原根存在性定理判断 n 是否有原根,复杂度 O(n)
  2. φ(n) 及其质因数集合,复杂度 O(n)
  3. 枚举 g[1,n] 中与 n 互质的数,根据原根判定性定理判定,得到最小原根 g ,复杂度 O(n14log2n)

第三步根据性质3的估计得到。

前两步可以换成筛法预处理,复杂度更大但常数小,时间上差不多,按需求改即可。

代码在求所有原根中给出。

时间复杂度 O(n)

空间复杂度 O(n)

枚举法(所有原根)

先求出最小原根,根据性质2的证明,枚举 k[1,φ(n)] 中与 φ(n) 互质的数,gk 即为原根。

可以换成 bitset 进行 [1,n] 递推,常数会小很多。

时间复杂度 O(nlogn)

空间复杂度 O(n)

int one_euler(int n) {
int ans = n;
for (int i = 2;i * i <= n;i++) {
if (!(n % i)) {
ans = ans / i * (i - 1);
while (!(n % i)) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
}
void get_pfactor(int n, vector<int> &pfactor) {
for (int i = 2;i * i <= n;i++) {
if (!(n % i)) {
pfactor.push_back(i);
while (!(n % i)) n /= i;
}
}
if (n > 1) pfactor.push_back(n);
}
int qpow(int a, ll k, int P) {
int ans = 1;
while (k) {
if (k & 1) ans = 1LL * ans * a % P;
k >>= 1;
a = 1LL * a * a % P;
}
return ans;
}
bool exist_proot(int n) {
if (n == 2 || n == 4) return true;
vector<int> pfactor;
if (n & 1) get_pfactor(n, pfactor);
else get_pfactor(n / 2, pfactor);
return pfactor.size() == 1;
}
int min_proot(int n) {
if (!exist_proot(n)) return 0;
int phi_n = one_euler(n);
vector<int> pfactor;
get_pfactor(phi_n, pfactor);
for (int i = 1;i <= n;i++) {
if (gcd(i, n) != 1) continue;
bool ok = 1;
for (auto j : pfactor) ok &= qpow(i, phi_n / j, n) != 1;
if (ok) return i;
}
return 0;
}
void get_proot(int n, vector<int> &proot) {
int g = min_proot(n);
if (!g) return;
int phi_n = one_euler(n);
for (int i = 1;i <= phi_n;i++) {
if (gcd(i, phi_n) != 1) continue;
proot.push_back(qpow(g, i, n));
}
}

指标

指标的定义与基本性质

定义g 为模 m 的原根,存在唯一整数 x[0,φ(m)) 满足 agx(modm) ,称这个 xam 关于 g 的指标(离散对数、指数),记作 indg(a)

离散对数和实数对数运算规则几乎没有区别,但注意离散对数建立在模 m 的简化剩余系,域内元素都是可逆的。

设正整数 a,b ,满足 gcd(a,m)=gcd(b,m)=1g 是模 m 的原根。

性质1(唯一性) ab(modm)indg(a)=indg(b)

性质2(加法) indg(a)+indg(b)indg(ab)(modφ(m))

性质3(减法) indg(a)indg(b)indg(ab)(modφ(m))

性质4(乘法) 对于任意整数 nnindg(a)indg(an)(modφ(m))

性质5(除法) 若整数 n 满足 gcd(n,φ(m))=1 ,那么 1nindg(a)indgn(a)(modφ(m))

性质6(逆元)g 也是模 m 的原根,则 indg(g)φ(m) 的逆元是 indg(g)

性质7(换底公式)g 也是模 m 的原根,那么 indg(a)indg(g)indg(a)(modφ(m))

性质1的证明:

ab(modm)gindg(a)gindg(b)(modm)indg(a)indg(b)(modφ(m))indg(a)=indg(b)

性质2的证明:

显然, gindg(a)+indg(b)gindg(a)gindg(b)abgindg(ab)(modm)

根据性质1得 indg(a)+indg(b)indg(ab)(modφ(m))

性质3的证明:

显然 b 有模 m 的逆元,那么 gindg(a)indg(b)gindg(a)gindg(b)abgindg(ab)(modm)

根据性质1得 indg(a)indg(b)indg(ab)(modφ(m))

性质4的证明:

根据性质1,且 a 具有模 m的逆元,显然得证。

性质5的证明:

显然 n 具有模 φ(m) 的逆元。

根据性质4和定义, 1nindg(a)indg(a1n)indgn(a)(modφ(m))

性质6的证明:

根据性质4和定义,得 indg(g)indg(g)indg(gindg(g))indg(g)1(modφ(m)) ,符合逆元定义。

性质7的证明:

根据性质6, indg(g)φ(m) 的逆元存在。

indg(a)indg(g)x(modφ(m)) ,那么 agxindg(g)gx(modm)

根据定义 x=indg(a)

指标的求法

BSGS算法

BSGS算法求解该类问题:给定 a,b,mZ+ ,满足 gcd(a,m)=1 , 求 axb(modm) 的最小非负整数解 x

根据阶的相关性质,我们知道 a 的次方的最小循环节是 δm(a)φ(m)<m 。而循环节最坏情况为 m1 ,为了方便,我们直接在 x[0,m) 中找最小解即可,不影响复杂度。

我们考虑用分块代替直接枚举,设块大小为 B=m ,令 x=iBj ,其中 i[1,B],j[0,B1] ,那么 x[1,B2] 。显然 B2m ,可以覆盖除了 0 的所有情况,我们一开始特判 0 即可。

此时,原方程变为 aiBjb(modm) ,进一步变形 aiBbaj(modm) 。此时,我们枚举 i ,只要找到 j 使得方程成立,即可得到一个解。注意到 j[0,B1] ,可以将 bajj 的映射存下来,每次枚举 i 的时候查询等于 aiB 对应的 j 即可。

需要注意的是,我们要求最小的非负整数解,那么在保证 i 尽可能小之后,还要保证 j 尽可能大,因此在存映射的时候,遇到 baj 值相同的,我们存 j 较大的。

使用前确保 a,b<m

时间复杂度 O(m)

空间复杂度 O(m)

int BSGS(int a, int b, int P) {
if (1 % P == b % P) return 0;
unordered_map<int, int> ump;
int B = sqrt(P) + 1;
int aB = 1;
for (int i = 0;i <= B - 1;i++) {
ump[1LL * aB * b % P] = i;
aB = 1LL * aB * a % P;
}
for (int i = 1, val = aB;i <= B;i++) {
if (ump.count(val)) return 1LL * i * B - ump[val];
val = 1LL * val * aB % P;
}
return -1;
}

扩展BSGS算法

考虑方程 axb(modm) ,其中 gcd(a,m) 不一定为 1 ,求解最小非负整数解。

如果 gcd(a,m)=1 ,直接使用BSGS算法即可。

否则,设 d=gcd(a,m) ,那么 am 的乘法群中都是 d 的倍数,因此 b 要满足 db 才有解,否则无解。

现在,根据同余基本性质的同除性,方程等价于 adax1bd(modmd)

显然 ad 存在逆元,因此我们得到新的方程 ax1bd(ad)1(modmd) ,对这个方程递归做上面步骤即可,每层递归要将答案加 1 。这里加 1 可以证明不影响解是最小的。

时间复杂度 O(m+log2m)

空间复杂度 O(m)

int exgcd(int a, int b, int &x, int &y) {
if (!b) { x = 1, y = 0; return a; }
int d = exgcd(b, a % b, x, y);
x -= (a / b) * y, swap(x, y);
return d;
}
int inv(int a, int P) {
int x, y;
exgcd(a, P, x, y);
return (x % P + P) % P;
}
int BSGS(int a, int b, int P) {
if (1 % P == b) return 0;
unordered_map<int, int> ump;
int B = sqrt(P) + 1;
int aB = 1;
for (int i = 0;i <= B - 1;i++) {
ump[1LL * aB * b % P] = i;
aB = 1LL * aB * a % P;
}
for (int i = 1, val = aB;i <= B;i++) {
if (ump.count(val)) return 1LL * i * B - ump[val];
val = 1LL * val * aB % P;
}
return -1;
}
int exBSGS(int a, int b, int P) {
if (1 % P == b) return 0;
int d = gcd(a, P);
if (d == 1) return BSGS(a, b, P);
if (b % d) return -2e9;
int ans = exBSGS(a, 1LL * b / d * inv(a / d, P / d) % (P / d), P / d);
return ans + (ans != -1);
}
posted @   空白菌  阅读(594)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示