数论在\(\text{OI}\)中十分重要。html
什么是数论?算法
\[ 研究整数的理论 ——zzq \]数组
本文包含全部侧边目录中呈现的内容。app
下面直奔主题。函数
若\(a\)是\(b\)的因数,或\(b\)是\(a\)的倍数,则\(a\)整除\(b\),记做\(a\mid b\)。学习
关于整除,有如下几点:测试
一、若\(a\mid b\),\(b\mid c\),则\(a\mid c\)。ui
二、若\(a\mid b\),\(a\mid c\),则\(a\mid b+c\)。spa
三、若\(a\mid b\),对于任意的\(k \in \Z\),都有\(a\mid kb\)。code
(小学生都懂)
给定整数\(a\),\(b\)和模数\(p\),若\(a \bmod p = b \bmod p\),则称\(a\)和\(b\)在模\(p\)意义下同余,简写成\(a\equiv b \pmod p\)。
同余有如下性质:
一、\((a \bmod p) \plusmn (b \bmod p) = (a \plusmn b) \bmod p\)
二、\((a \bmod p) \times (b \bmod p) = (a \times b) \bmod p\)
三、\(\frac{a \bmod p}{b \bmod p} = \frac{a}{b} \bmod p\)(若是你直接算的话,数学老师会揍你,正确姿式放后面)
四、\(ax \equiv bx \pmod p \Leftrightarrow a \equiv b \pmod { \frac{ p }{ \gcd(x,p) } }\)
\(Proof\):
\[\because ax \equiv bx \pmod p\]
\[\therefore (a-b)x \equiv 0 \pmod p\]
\[\therefore p\mid (a-b)x\]
\[\therefore \frac{p}{\gcd(x,p)}\mid \frac{x}{\gcd(x,p)}(a-b)\]
\[\because \gcd(\frac{p}{\gcd(x,p)},\frac{x}{\gcd(x,p)})=1,也就是互质\]
\[\therefore \frac{p}{\gcd(x,p)} \mid (a-b)\]
\[\therefore a-b \equiv 0 \pmod{ \frac{p}{\gcd(x,p)} }\]
\[因而a\equiv b \pmod{ \frac{p}{\gcd(x,p)} }得证\]
先定义线性组合:形如\(ax+by\)的式子,其中\(a,b,x,y\in \Z\)。
裴蜀定理就是:若是\(a\)、\(b\)是不为\(0\)的任意整数,则\(\gcd(a,b)\)是线性组合\(ax+by\)的最小正整数。
\(Proof\):
\[由\gcd(a,b)\mid a和\gcd(a,b)\mid b得\]
\[\gcd(a,b)\mid ax+by\]
\[设d=\gcd(a,b),ax+by的最小正值为s,由d\mid ax+by恒成立获得d\mid s\]
\[令q=\lfloor \frac{a}{s} \rfloor,r=a \bmod s=a-q(ax+by)=a(1-qx)+b(-qy),发现r也为a,b的线性组合\]
\[又\because 0 \leq r < s\]
\[\therefore r = 0(最小正整数为s,而r<s,说明r=0)\]
\[\therefore q=\frac{a}{s},即s\mid a,相似的可获得s\mid b\]
\[\therefore s\mid ax+by,而d\mid ax+by\]
\[\therefore s = d\]
\[ax+by的最小正值为\gcd(a,b)得证\]
这必定理十分经常使用。
推论:\(\gcd(a,b)=\gcd(b, a \bmod b)\)。
\(Proof\):
\[由裴蜀定理得ax+by的最小正整数为\gcd(a,b) \tag{*}\]
\[同时获得bx+(a \bmod b)y的最小正整数为\gcd(b, a\bmod b) \tag{**}\]
\[而bx+(a \bmod b)y = bx + (a - \lfloor \frac{a}{b} \rfloor \times b)y=bx+ay-\lfloor \frac{a}{b}\rfloor by = ay+b(x-\lfloor \frac{a}{b}\rfloor y)\]
\[该式又是关于a,b的线性组合,最小正值为\gcd(a,b),与(*),(**)同样。\]
同时,这个跟\(Ex-GCD\)的推导大同小异:为了求解\(ax+by=\gcd(a,b)\)方程,利用递归求解,即新的\(x'=y,y'=x-\lfloor \frac{a}{b} \rfloor y\),当\(b=0\)时是边界,显然\(x=1,y=0\),此时知足\(ax+by=\gcd(a,b)=a\)。
相似的,也能够获得:
推论\(1\):\(\forall a,b\in \Z : \gcd(a,b)=\gcd(a-b,b)\);
推论\(2\):\(\forall a,b为偶数:\gcd(a,b)=2 \times \gcd(a/2,b/2)\);
推论\(3\):\(\forall a为偶数,b为奇数:\gcd(a,b)=\gcd(a/2,b)\)。以上式子能够进一步推广成:若\(a=2^i \times a'\),\(b=2^j \times b'\),则\(\gcd(a,b)=2^{\min(i,j)} \times \gcd(a',b')\)。
证实略
\((G,·)\)表示\(G\)对定义在集合\(G\)上的运算\(·\)构成群,它须要知足:
(1)封闭性:若\(a,b \in G\),则存在惟一肯定的\(c \in G\),使得\(a·b=c\)。
(2)结合律:对于\(a,b,c \in G : (a·b)·c = a·(b·c)\)
(3)存在单位元:存在\(e \in G\),使得\(\forall a \in G : a·e=e·a\),\(e\)被称做单位元。
(4)存在逆元:\(\forall a \in G, \exist b \in G : a·b=b·a=e\),\(b\)又写做\(a^{-1}\)。
根据群中的理论,在模\(p\)意义下,对于一个数\(a\),若是存在一个数\(b\),使得\(a \times b \equiv 1 \pmod p\),则\(b\)是\(a\)的逆元,记做\(a^{-1}\)。同时,\(a\)和\(a^{-1}\)互为逆元。
逆元存在的条件:\((a,p)=1\)。
\(Proof\):
\[上式可当作ax+py=1,只不过y应为负数\]
\[由裴蜀定理得:(a,p)\mid 1,因此(a,p)=1,\text{Q.E.D}\]
上面的\(\frac{a}{b}\)能够写成\(a\times b^{-1}\),假设逆元存在,因而前面式\(3\)就能够成了。
一、存在惟一性
模\(p\)意义下,对于\(a\)而言,若是其存在逆元,则逆元只有一个\(a'=a^{-1}\)。
证实很显然:假设存在另外一个逆元\(a''\times a \equiv 1\),由\(a'\ \times a \equiv 1 \pmod p\)得:\((a''-a') \times a \equiv 0 \pmod p\),有\(a \neq 0\),因此有\(a''-a'\equiv 0 \pmod p\),因而\(a' \equiv a'' \pmod p\)。
二、彻底积性函数
也就是\((ab)^{-1} \equiv a^{-1} \times b^{-1} \pmod p\)。
证实也很简单:考虑有\(a \times a^{-1} \equiv 1 \pmod p\)和\(b \times b^{-1} \equiv 1 \pmod p\),乘到一块儿就是\((ab)(a^{-1}\times b^{-1}) \equiv 1 \pmod p\),因此\((ab)^{-1} \equiv a^{-1}\times b^{-1} \pmod p\)。
如何求解逆元呢?
一个一个试呗(很少说)
复杂度:\(\text{O}(p)\)。
定理:当\(p\)为素数时,\(a^{p-1}\equiv 1 \pmod p\)
\(Proof\):
\[构造一个缩系A=\{x\mid (x,p)=1\},则\mid A\mid =\varphi(p)=p-1,\varphi(p)表示与p互质的数个数\]
\[对于a \neq 0构造集合B=\{ax\mid x \in A\},易证在元素模p意义下:A=B\]
\[先证\forall x \in B,x' \in A:x由惟一的x'·a获得\]
\[与证惟一性同样,假设有x''·a\equiv x,则有(x'-x'')a \equiv 0,有a\neq 0,故有x\equiv x'',因此惟一\]
\[\therefore 在模p意义下,\prod_{x \in A}x \equiv \prod_{y \in B}y \equiv \prod_{x \in A}ax \equiv a^{p-1}\prod_{x \in A}x \pmod p\]
\[即证a^{p-1}\equiv 1 \pmod p\]
因此要求\(a^{-1}\),只需求\(a^{p-2}\)便可。
复杂度:\(\text{O}(\log p)\)。
这里能够推广成\(a^{\varphi(p)}\equiv 1 \pmod p\)。(缩系大小为\(\varphi(p)\))
写个式子你就明白了:\(ax+py=1\),\(x\)就是\(a^{-1}\)。
复杂度:\(\text{O}(\log p)\)。
由彻底积性的性质获得的方法。这个方法能够批量求解,可是对于\(p\)是素数的状况下仍然须要前面的方法,复杂度\(\text{O}(n+素数\times \log n)\)
假如咱们要求\(a\)在模\(p\)意义下的逆元,构造式子\(p=qa+r\),发现\(q=\lfloor \frac{p}{a} \rfloor\),\(r=p \bmod a\)。
而后咱们有:
\[r\times r^{-1} \equiv 1 \pmod p\]
用\(r=p-qa\)替换得:
\[(p-qa)\times r^{-1} \equiv 1 \pmod p\]
而后:
\[-qa\times r^{-1} \equiv 1 \pmod p\]
因此咱们就有了:
\[a^{-1} \equiv -q \times r^{-1} \pmod p\]
用最上面的东西替换就获得了:
\[a^{-1} \equiv -\lfloor \frac{p}{a} \rfloor \times (p \bmod a)^{-1} \pmod p\]
因而咱们就能够线性求解了。
n = read(), p = read(); // 咱们要求1~n在模p意义下的逆元 inv[1] = 1; // 首先1的逆元为1 rep(i, 2, n) inv[i] = (ll)(p-p/i) * inv[p % i] % p; // 日后线性推出来
复杂度:\(\text{O}(p)\)。
可是这里有缺陷:就是逆元必须从前日后推,若是随机性地从中挑些数出来询问,而\(p\)又很大,就会\(\text{T}\)。因此根据题目须要进行选择(可能还须要巧妙的方法)。
前提是获得了模数\(p\)和要询问的数组\(A\),要求\(A\)的逆元。
令\(S=\prod A_i\),\(A_i\)的逆元就是\(\frac{S/A_i}{S}\)。咱们不但愿用费马小定理求屡次逆元,这样复杂度很大。咱们只要求出\(S^{-1}\)便可,这一步的复杂度为\(\text{O}(\log p)\)。
对于\(S/A_i\),咱们维护一个前缀\(P_i=\prod_{x<i}A_x\),维护一个后缀\(Q_i=\prod_{x>i}A_x\),则\(S/A_i = P_i\times Q_i\)(正好少乘了一个\(A_i\)),因而问题得解。这一步的复杂度只与查询数多少有关,为\(\text{O}(n)\)。
总复杂度:\(\text{O}(n+\log p)\)。正好弥补了解法\(5\)的缺陷。
即求出\(ax\equiv b \pmod p\)的解。
首先根据裴蜀定理必需要有\((a,p)\mid b\),要否则无解。若是\((a,p)=1\),显然\(a\)存在逆元,则\(x \equiv ba^{-1} \pmod p\);
若是\((a,p)\neq 1\),令\(d=(a,p)\),
\[\because d\mid a, d\mid p, d\mid b\]
\[\therefore 由同余性质获得:\frac{a}{d} x \equiv \frac{b}{d}\pmod{ \frac{p}{d} }\]
令\(a'=\frac{a}{d}\),\(b'=\frac{b}{d}\),\(p'=\frac{p}{d}\),此时有\((a', p')=1\),\(a'\)存在逆元,因此\(x \equiv b'a'^{-1} \pmod {p'}\)。若是逆元不能用费马定理求解(好比说\(p'\)不是质数),就用\(\text{Ex-GCD}\)求解。
前面提过:与\(n\)互质的数的个数,记做\(\varphi(n)\)。
这个函数的求解:\(\varphi(n)=n\prod_{p_i}(1-\frac{1}{p_i})\),其中\(p_i\)是\(n\)的素因子。
\(Proof\):
\[咱们用容斥\]
\[\varphi(n)=n-\frac{n}{p_1}-\frac{n}{p_2}-\dotsb+\frac{n}{p_1p_2}+\frac{n}{p_1p_3}-\dotsb\frac{n}{p_1p_2p_3}-\dotsb\]
\[(这里全部的除法均能整除)\]
\[提出其中一个素因子:\]
\[ \begin{array}{ll} \varphi(n)&=n[(1-\frac{1}{p_1}-\frac{1}{p_2}\dotsb-\frac{1}{p_{s-1}}+\frac{1}{p_1p_2}+\frac{1}{p_1p_3}+\dotsb)-\frac{1}{p_s}(1-\frac{1}{p_1}-\frac{1}{p_2}\dotsb-\frac{1}{p_{s-1}}+\frac{1}{p_1p_2}+\frac{1}{p_1p_3}+\dotsb)]\\ \\ &=n(1-\frac{1}{p_s})(1-\frac{1}{p_1}-\frac{1}{p_2}\dotsb-\frac{1}{p_{s-1}}+\frac{1}{p_1p_2}+\frac{1}{p_1p_3}+\dotsb)\\ \\ &=\dotsb \\ \\ &=n\prod_{p_i}(1-\frac{1}{p_i}) \end{array} \]
解释下:就是每次在容斥的式子里面提出一个素因子,剩下的因式与没有该素因子的式子同样,能够合并,因而命题得证。
由此,咱们看出\(\varphi(n)\)是积性函数,能够用线性筛求解。
const int N = 10000000; int p[N], check[N], phi[N]; void init() { check[1] = 1; phi[1] = 1; rep(i, 2, N) { if (!check[i]) phi[p[++p[0]] = i] = i-1; rep(j, 1, p[0]) { if (i*p[j] > N) break; check[i*p[j]] = 1; if (!(i % p[j])) { phi[i*p[j]] = phi[i] * p[j]; break; } // 质因数p[j]在i中已经出现,互质个数增长p[j]倍 else phi[i*p[j]] = phi[i] * (p[j] - 1); // 不然乘上p[j]-1 } } }
当\(n>2\)时,互质的数是成对出现的,若\((n,m)=1\),则\((n,n-m)=1\),显然\(m\neq n-m\),这一有用的性质能够证实:全部与\(n\)互质的数的和为\(\frac{n\varphi(n)}{2}\)。\(n\leq 2\)时特殊代入一下便可。
上面提过:当\((a,n)=1\)时,有\(a^{\varphi(n)}\equiv 1 \pmod n\),证实略了。
还有拓展欧拉定理:任意状况下,\(a^{b+\varphi(n)} \equiv a^{b \bmod \varphi(n) + \varphi(n)} \pmod n\)。咱们采用分解化归再合并的方法证实该命题。
\(Proof\):
\[对于(a,n)\neq 1,取a的质因子p,将n分解使得n=s\times p^r,且(a,s)=1\]
\[由积性函数性质得:\varphi(s)\mid \varphi(n)\]
\[\therefore 有p^{\varphi(n)} \equiv 1 \pmod s\]
\[\forall k \in \Z:p^{k\varphi(n)} \equiv 1 \pmod s\]
\[由同余的性质4:p^{k\varphi(n)} \times p^r \equiv p^r \pmod {s\times p^r}\]
\[\therefore p^{k\varphi(n)+r} \equiv p^r \pmod n\]
\[\therefore \forall c \in \Z_+:p^{k\varphi(n)+r+c} \equiv p^{r+c} \pmod n\]
\[也就是说:\forall c\in \Z, c\geq r:p^c \equiv p^{k\varphi(n)+c} \pmod n\]
\[对于a=p_1^{\alpha_1}p_2^{\alpha_2}\dotsb,先考虑其中任意一个因子p^k,p是素数,假设r\leq \varphi(n) \leq c,有:\]
\[(p^k)^c \equiv p^{kc} \equiv p^{kt\varphi(n)+kc} \equiv (p^k)^{t\varphi(n)+c} \pmod n\]
\[综上获得:(p^k)^c \equiv (p^k)^{c \bmod{\varphi(n)} + \varphi(n)},加上一个\varphi(n)是为了避免让指数<c。\]
\[将全部质因子乘起来:(\prod_{p_i}p_i^{\alpha_i})^c \equiv \prod_{p_i}(p_i^{\alpha_i})^c \equiv \prod_{p_i}(p_i^{\alpha_i})^{c \bmod \varphi(n) + \varphi(n)} \equiv (\prod_{p_i}p_i^{\alpha_i})^{c \bmod \varphi(n) + \varphi(n)} \pmod n\]
\[用b+\varphi(n)替换c(由于有c\geq \varphi(n)),即证a^{b+\varphi(n)} \equiv a^{b \bmod \varphi(n) + \varphi(n)}\]
若\(p\)是质数,恒有\((p-1)! \equiv -1 \pmod p\)成立。
\(Proof\):
\[发现a和a^{-1}互为逆元,获得逆元的成对出现\]
\[但若是a\equiv a^{-1}怎么办,咱们能够求出来\]
\[也就是说aa^{-1}\equiv a^2 \equiv 1 \pmod p\]
\[咱们变个形:a^2-1 \equiv 0 \pmod p\]
\[也就是:p \mid a^2-1 \Rightarrow p \mid (a-1)(a+1)\]
\[\therefore a-1 \equiv 0 \pmod p 或者 a+1 \equiv 0 \pmod p,不可能存在第三者由于p是质数\]
\[获得a \equiv 1 \pmod p或a\equiv -1 \pmod p\]
\[对于p=2,两式等价,(p-1)!\equiv 1 \equiv p-1 \pmod p,成立\]
\[对于p\neq 2,有两解为\pm 1(1或p-1)\]
\[对于其它数必定能与逆元配对,乘积为1\]
\[\therefore (p-1)! \equiv 1^{\frac{p-3}{2}} \times 1 \times -1 \equiv -1 \pmod p,这里\frac{p-3}{2}指逆元配对数\]
\[\text{Q.E.D}\]
用来求解:
\[ \begin{cases} x \equiv a_1 \pmod {p_1} \\ x \equiv a_2 \pmod {p_2} \\ \dotsb \\ x \equiv a_n \pmod {p_n} \\ \end{cases} \]
的最小正整数解(或者通解)。
让\(M=\prod p_i\),\(m_i = \frac{M}{p_i}\),令\(m_i^{-1}\)为\(m_i\)在模\(p_i\)意义下的逆元,则有\(m_im_i^{-1} \equiv 1 \pmod {p_i}\),即\(a_im_im_i^{-1} \equiv a_i \pmod {p_i}\),将\(a_im_im_i^{-1}\)记做\(L_i\)。这里能够看出\(m_i^{-1}\)的存在必须知足:\((m_i,p_i)=1\),也就是\(p_i\)两两互质才行。
构造\(x=\sum L_i\),\(x \bmod M\)就是同余方程组的最小正整数解。
\(\forall i,j:L_j \bmod p_i \equiv \begin{cases} 0,j \neq i(m_i中有因子p_i)\\ a_i,j=i(由上面定义可知)\end{cases}\)
N = read(); M = 1; rep(i, 1, N) { p[i] = read(), a[i] = read(); // p为模数,a为余数 M *= p[i]; } ans = 0; rep(i, 1, N) { ll m = M / p[i], invm, y, d; exgcd(m, p[i], invm, y, d); // 求出mi和mi^-1 ans = (ans + (ll)a[i] * m % M * (invm+p[i])) % M; // 加起来 } printf("%lld", ans);
构造的局限性也很显然:模数要两两互质才行,不然没法构造。
复杂度:\(\text{O}(n\log (\prod p_i))\),由于要求逆。
构造能够应用于各类情形(不互质也ok)。思想就是将两个同余式合并成一个同余式,合并\(n-1\)次只剩下一个同余式,就是咱们要的答案。
令\(k_i=\text{LCM}_{x\leq i}\ b_x\),假设咱们合并了前面的\(i-1\)个方程,通解为\(x\),也就是说这个\(x\)代入前\(i-1\)个方程恒成立,为了让第\(i\)个方程成立,咱们有:
\[x+t\times k_{i-1}\equiv a_i \pmod {p_i}\]
记\(x'=x+t\times k_{i-1}\),发现\(x'\)对于前\(i\)个式子恒成立,并且\(k_{i-1}\)与前\(i-1\)个模数取余为\(0\)。解出来\(t\),而后一直算下去。
可是不互质时可能会无解,当且仅当\((k_{i-1}, p_i)\nmid a_i-x\)。
int N; ll a[maxn], p[maxn]; void inc(ll &a, ll b, ll p) { // 同余加 a += b; if (a >= p) a -= p; } ll dec(ll v, ll p) { return v < 0 ? v+p : v; } // 同余减后的处理 ll Mult(ll a, ll b, ll p) { // 快(gui)速乘 ll res = 0; for (ll k = a; b; inc(k, k, p), b >>= 1) if (b & 1) inc(res, k, p); return res; } void exgcd(ll a, ll b, ll &x, ll &y, ll &d) { b ? (exgcd(b, a % b, y, x, d), y -= a/b*x) : (x = 1, y = 0, d = a); // 求ax+by=gcd(a,b)的一组解 } void excrt() { ll x = a[1], k = p[1]; // 初始值 rep(i, 2, N) { ll t, y, d, c, _k; exgcd(k, p[i], t, y, d); // 求解方程t*k+p_i*y=(k,p_i)的解 if ((c = dec(a[i]-x%p[i], p[i])) % d) return; // 若是(k,p_i)不整除c,同余方程无解 _k = k, k = k/d*p[i]; // 用_k记录原来的k t = Mult((t+p[i])%p[i], c/d, p[i]); // 更新t的值(按比例放大) inc(x, Mult(t, _k, k), k); // 用x'代替x } printf("%lld", x); }
定义原根以前,咱们先定义个东西。
整数的阶:给定\(a\),求使得\(a^x \equiv 1 \pmod p\)成立的最小的正整数\(x\),这个\(x\)成为\(a\)模\(p\)的阶,同时要知足\((a,p)=1\),记做\(\text{ord}_pa\)。由欧拉定理易知它确定存在,且能推出\(\text{ord}_pa \mid \varphi(p)\)。
阶的性质:
一、\(\text{ord}_pa \mid \varphi(p)\)。
二、\(a\),\(a^2\),\(a^3\dotsb a^{\text{ord}_pa}\)在模\(p\)意义下互不相等。显然若是相等会违背阶的定义。
三、\(a^r \equiv a^{r'} \pmod p \Leftrightarrow r \equiv r' \pmod{\text{ord}_pa}\)。一样很显然。
四、\(\text{ord}_ma^k=\frac{\text{ord}_ma}{(\text{ord}_ma,k)}\)。证实用一下裴蜀定理便可。
五、\(\text{ord}_m(ab) = \text{ord}_ma \times \text{ord}_mb \Leftrightarrow (\text{ord}_ma, \text{ord}_mb) = 1\)。用整除和阶的性质证一证便可。
如何求\(\text{ord}_pa\)?枚举\(\varphi(p)\)的质因子试除判断便可。
// 事先咱们求出了phi[N],pri表示分解phi[N]获得的质因数 int ord(int n) { int ret = phi[N]; for (register int i = 0; i < (int)pri.size(); ++i) { if (qpow(n, ret/pri[i], N) == 1) ret /= pri[i]; // 试除成功 } return ret; }
原根:若\(\text{ord}_pa=\varphi(p)\)时,则称\(a\)是\(p\)的原根。
有原根的数为\(1\),\(2\),\(4\),\(t\),\(2t\),\(t^k\),\(2t^k\),其中\(t\)为奇素数,\(k \in \Z_+\)。
由此咱们能够看出:原根\(g\)的\(t\)次方在模\(p\)意义下会遍历全部\(\varphi(p)\)个与\(p\)互质的数。
若是在模数\(p\)下有原根,则原根的个数为\(\varphi(\varphi(p))\)。
\(Proof\):
\[咱们找一个任意的原根g,这个g的任意次方构成p的缩系,大小为\mid \varphi(p) \mid\]
\[且根据阶的定义,会获得\{g,g^2,g^3,\dotsb g^{\varphi(p)}\},两两不一样\]
\[由于有g^{\varphi(p)}\equiv 1,因此g^x \equiv g^{x \bmod \varphi(p)},构成一个环\]
\[咱们考虑其中哪些元素是原根\]
\[设原根为g',易知其指数\leq \varphi(p)时,会取遍缩系中元素\]
\[由于g'在缩系中,因此g'能够表示成g^k,其中1\leq k \leq \varphi(p)\]
\[g'是原根当且仅当g^k,g^{2k},\dotsb,g^{k\varphi(p)}两两不一样\]
\[等价于k,2k,\dotsb,k\varphi(p)在模\varphi(p)意义下两两不一样\]
\[命题的成立必须知足(k,\varphi(p))=1\]
\[能够理解成从环上起点开始每次走k步走回到起点的最小次数为\varphi(p)\]
\[也就是解kx \equiv 0 \pmod{\varphi(p)},获得最小正数解x=\frac{\varphi(p)}{(\varphi(p),k)}\]
\[而使得x=\varphi(p),当且仅当(\varphi(p),k)=1\]
\[这样的k有\varphi(\varphi(p))个,即证原根个数只有\varphi(\varphi(p))个\]
经过这个还能够证在缩系中,若是\(d \mid \varphi(p)\),则恰有\(\varphi(d)\)个数的阶为\(d\),这里不证了(同上)。
判断\(a\)是否为原根?一样试除。因为\(\text{ord}_pa \mid \varphi(p)\),咱们枚举\(\varphi(p)\)的质因子\(p_i\)试除而后看\(a^{\frac{\text{ord}_pa}{p_i}}\)是否同余为\(1\),若是对于全部质因子除完后都不成立,则\(a\)是\(p\)的原根。证实略。
如何求原根?原根比较多,且相对分布均匀,因此直接暴力找。
\(\text{BSGS}\)(\(\text{Baby Step Giant Step}\))是用来解决\(a^x \equiv b \pmod p\)的问题。
令\(m=\lceil \sqrt{p} \rceil\),则\(x\)能够表示成\(mq+r\),其中\(0 \leq q \leq m\),\(0 \leq r < m\)。
那么\(a^x\equiv b \pmod p\)能够写成\(a^{mq+r} \equiv b \pmod p\)。
移项得:\(a^{mq} \equiv ba^{-r} \pmod p\)。
如今问题变成了:找出一组\(q\),\(r\),使得上式成立,咱们枚举\(q\),将左边获得的式子的全部种取值哈希起来,而后再枚举\(r\),判断右边的取值是否在左边的取值中出现,若是出现就找到了解。
枚举部分的复杂度\(\text{O}(\sqrt p)\),求逆元的复杂度为\(\text{O}(\log p)\),因此总复杂度为\(\text{O}(\sqrt p + \log p)\)。
若是你不肯意求逆也能够,只要设\(x=mq-r\),而后\(0 < q \leq m\),\(0 \leq r < m\),而后移项过程当中\(-r\)变成\(+r\),因而就不用逆元了。
// 前面定义了Hash类,支持不重复插入和查询 int BSGS(int a, int b) { // 解a^x = b if (a == 0) return b ? -1 : 1; Hash::clear(); // 清空 int m = (int)ceil(sqrt(P+0.5)); // 尽量使得m大 int am = qpow(a, m), t = 1; // 求a^m rep(i, 0, m) { Hash::insert(t, i); // 一个一个插入 t = (ll)t * am % P; } t = b; int inva = inv(a), ans = -1, q; // 求出a的逆元,标记答案 rep(r, 0, m-1) { if (~(q = Hash::exist(t))) { // 哈希表存在t int x = m*q + r; // 求解指数 if (ans == -1 || x < ans) ans = x; // 更新答案(可能不是最小的指数) } t = (ll)t * inva % P; } return ans; // 若是ans=-1代表无解;反之有解且为最小解。 }
这种状况下,\(a\)的逆元不存在,因此咱们要约分两边使得\(a\)的逆元出现。
对于\(a^x \equiv b \pmod p\),提出一个\(d=(a,p)\)并约分获得\(a^{x-1}\frac{a}{d} \equiv \frac{b}{d} \pmod {\frac{p}{d}}\)。固然要有\(d \mid b\),不然无解。
继续,若是\(d'=(a,\frac{p}{d}) \neq 1\),再拿出一个\(a\)提取因子,获得\(a^{x-2}\frac{a}{d}\frac{a}{d'} \equiv \frac{b}{dd'} \pmod {\frac{p}{dd'}}\)
以此类推,直到其互质,此时有\(a^{x-t}\frac{a^t}{\prod d} \equiv \frac{b}{\prod d} \pmod {\frac{p}{\prod d}}\)
显然此时\(\frac{a^t}{\prod d}\)存在逆元,因而能够移过去获得\(a^{x-t} \equiv \frac{b}{\prod d}\frac{a^{-t}}{\prod d} \pmod {\frac{p}{\prod d}}\),而后就成了上面的问题。
这里约分时因为约分和求公约数,多了一个\(\text{O}(\log^2 p)\)的复杂度,其余不变。
int exBSGS(int a, int b, int p) { int t = 0, d = gcd(a, p), l = 1; // 如上所述,l表示约分后左边的因子 while (d > 1) { if (b % d) return -1; // 若是不能整除说明必定无解 t++; p /= d, b /= d; // 约分,并记录指数 l = (ll)l * (a / d) % p; // d = gcd(a, p); } if (!l) return t; // 若是左边的因子为0,显然有a=0,b=0(同余意义下),而后解就是t,这个特判掉 a %= p, b = (ll)b * inv(l, p) % p; // 移项 int ans = BSGS(a, b, p); // 转化成了普通的bsgs问题 return ~ans ? ans+t : -1; // 若是无解返回-1,有解返回ans+t }
解决形如\(x^k \equiv a \pmod p\)的问题。暴力找彷佛代价过高了。咱们先从简单的入手。
若\(x^2 \equiv a \pmod p\)有解,则称\(a\)是\(p\)的二次剩余。不然称其为非二次剩余。
定义\(\text{Legendre}\)符号:
\[ \left( \frac{a}{p} \right)=\begin{cases} 1 & a是p的二次剩余 \\ -1 & a不是p的二次剩余 \\ 0 & p \mid a \end{cases} \]
(长相奇怪,其实只是记号)
先讨论\(p\)是奇素数的状况。此时有:
\[\left( \frac{a}{p} \right) \equiv a^\frac{p-1}{2} \pmod p\]
\(Proof\):
\[当p \mid a时命题显然成立\]
\[当p \nmid a时,若是a是p的二次剩余,则有x^2\equiv a \pmod p\]
\[\therefore (x^2)^{\frac{p-1}{2}} \equiv a^{\frac{p-1}{2}} \pmod p\]
\[\therefore x^{p-1} \equiv a^{\frac{p-1}{2}} \pmod p\]
\[由费马小定理易知左边同余1,因此右边同余1\]
\[若是a不是p的二次剩余,则不存在x使得x^2 \equiv a \pmod p成立\]
\[\because p是奇质数\]
\[\therefore 必定能找到x \neq y,使得xy \equiv a \pmod p\]
\[而共有\varphi(p)=p-1个数,其能够两两配对使得上式成立(由逆元的惟一性获得)\]
\[\therefore (p-1)! \equiv a^{\frac{p-1}{2}} \pmod p\]
\[又由威尔逊定理得:(p-1)! \equiv -1 \pmod p\]
\[\therefore a^{\frac{p-1}{2}} \equiv -1 \pmod p\]
咱们证实了全部的二次剩余获得的结果都是\(1\),全部的非二次剩余获得的结果都是\(0\),而又不存在第三者,因此命题能成立。
而后有\(p\)是奇质数下,二次剩余和非二次剩余的数量同样多。这个很显然,让\(1\)到\(p-1\)的平方在模\(p\)意义下构成集合,而咱们有:
\[x^2 \equiv (p-x)^2 \pmod p\]
这个东西展开下就证完了,因此咱们只要考虑\(1\)到\(\frac{p-1}{2}\)之间的平方便可。
如有\(x^2 \equiv y^2 \pmod p\),\(x \neq y\),则\(x^2-y^2 \equiv 0 \pmod p \Rightarrow p \mid (x-y)(x+y)\),因为\(p\)是质数,因此有\(p \mid x-y\)或\(p \mid x+y\)而获得\(x \equiv y\)或\(-y\),第一种状况不存在,第二种状况发现两数必定不在同一个部分(即在\(\frac{p-1}{2}\)两边),不合假设。因此获得\(1\)到\(\frac{p-1}{2}\)之间,两两的平方不一样,而这样获得的二次剩余只有\(\frac{p-1}{2}\)个,非二次剩余也只有\(\frac{p-1}{2}\)个。因此得证。
该算法是用来解决二次剩余问题的,思想基于构造。注意下文解决的是\(x^2 \equiv n \pmod p\),右边的是\(n\)而不是\(a\)了。
若是\(n\)是二次非剩余,显然无解;
若是\(n\)是二次剩余,咱们随机一个\(a\),使得\(\left( \frac{a^2-n}{2} \right) = -1\)。由非二次剩余和二次剩余数量同样多,因此有\(\frac{1}{2}\)的几率取到,而指望下\(2\)次即能获得。证实不重要,放在后面。定义\(\omega = \sqrt{a^2-n}\),事实上\(\omega\)在整数域中没有数存在,因此相似于定义虚数单位\(i\)同样,存数也先存成\(a+b\omega\)这种形式,以后会消掉这个东西。
咱们要\(x^2 \equiv a \pmod p\),先要知道这几个引理。
引理1:\((x+\omega)^p \equiv x^p + \omega^p \pmod p\)。
\(Proof\):
\[由二项式定理得:(x+\omega)^p = \sum_{i=0}^p {p \choose i} x^i\omega^{p-i} \equiv x^p+\omega^p \pmod p\]
\[中间若干项项因为组合数系数被模掉了,这样就\text{Q.E.D}了\]
引理2:\(\omega^p \equiv -\omega\)。
\(Proof\):
\[注意\omega可能不存在于整数域中,因此咱们不能直接运用费马小定理\]
\[\omega^p = \omega^{p-1}\omega = (\omega^2)^\frac{p-1}{2}\omega = (a^2-n)^\frac{p-1}{2}\omega \equiv -\omega \pmod p\]
\[这些咱们运用的是上面的定义推导出来的\]
结论:\(x=(a+\omega)^\frac{p+1}{2}\)
\(Proof\):
\[(a+\omega)^{p+1} = (a+\omega)^p(a+\omega) = (a^p+\omega^p)(a+\omega)=(a-\omega)(a+\omega)=a^2-\omega^2=a^2-(a^2-n)=n \equiv x^2 \pmod p\]
\[\Rightarrow (a+\omega)^{p+1} \equiv x^2 \pmod p\]
\[\Rightarrow x \equiv (a+\omega)^\frac{p+1}{2} \pmod p\]
\[\text{Q.E.D}\]
那这个式子咋求?直接上复数。根据拉格朗日定理,最后虚部必定为\(0\)。不会拉格朗日定理?其实我也不会那咱们反证下吧。
\(Proof\):
\[假设存在x=A+B\omega,B \neq 0,使得x^2 = (A+B\omega)^2 \equiv n \pmod p\]
\[那么必定有A^2 + 2AB\omega + B^2\omega^2 = A^2 + 2AB\omega + B^2(a^2-n) \equiv n \pmod p\]
\[\Rightarrow A^2+B^2(a^2-n)-n \equiv -2AB\omega \pmod p\]
\[\because式子左边的虚部为0,代表式子的右边虚部也为0\]
\[\therefore必须有:-2AB \equiv 0 \pmod p\]
\[\because B \not\equiv 0\]
\[\therefore A \equiv 0\]
\[\therefore B^2\omega^2 \equiv n \pmod p\]
\[\therefore \omega^2 \equiv nB^{-2} \pmod p\]
\[n是二次剩余,B^2是二次剩余,因此B^{-2}也应为二次剩余\]
\[根据\text{Legendre}符号能够推出二次剩余与二次剩余的积为二次剩余,而\omega^2却不是二次剩余\]
\[矛盾,因此B=0\]
还有一种状况就是\(p=2\),此时它是质数但不是奇质数,因此特判掉便可。
若是\(x\)有解,其实存在两组解,另外一组解为\(p-x\)。
int T, N, P; namespace Cipo { #define rd() (1ll * rand() * RAND_MAX + rand()) // 随机数可能范围不够 int n, P; // x^2=n (%p) int a, t; // t = w^2 = a^2-n struct comp { // 自定义复数类 int x, y; comp operator * (const comp &rhs) const { // (x1,y1)(x2,y2)=(x1x2+y1y2t,x1y2+x2y1),这是复数乘,注意取模 return (comp){(1ll*x*rhs.x + 1ll*y*rhs.y%P*t)%P, (1ll*x*rhs.y+1ll*y*rhs.x)%P}; } }; comp qpow(comp a, int b) { // 复数类快速乘 comp res = (comp){1, 0}; for (comp k = a; b; k = k*k, b >>= 1) if (b & 1) res = res*k; return res; } int qpow(int a, int b) { // 普通快速乘 int res = 1; for (register int k = a; b; k = (ll)k*k%P, b >>= 1) if (b & 1) res = (ll)res*k%P; return res; } int sqrt(int n, int P) { // 开根号 Cipo::n = n, Cipo::P = P; if (qpow(n, (P-1)>>1) == P-1) return -1; // n不是二次剩余 while (a = rd() % P, qpow(t = (1ll*a*a-n+P)%P, (P-1)>>1) == 1); // 判断a^2-n获得的是否是二次剩余,若是是从新随机a return qpow((comp){a, 1}, (P+1)>>1).x; // 返回结果 } } int main() { srand(time(0)); T = read(); while (T--) { N = read(), P = read(); N %= P; if (!N) { printf("0\n"); continue; } // 若是n=0代表x=0(%p) if (P == 2) { printf("1\n"); continue; } // p=2特判 int ans = Cipo::sqrt(N, P); // 开根 if (ans == -1) printf("Hola!\n"); // 无解 else printf("%d %d\n", min(ans, P-ans), max(ans, P-ans)); // 不然有两解 } return 0; }
该算法的复杂度:\(\text{O}(\log p)\)。
附指望是\(2\)的证实:
\[ \sum_{i=1}^{\infty}\frac{1}{2^i}\times i (这个由指望的定义来:几率×次数)\\ =\sum_{i=0}^{\infty}\frac{1}{2^i} + \frac{1}{2}\sum_{i=0}^{\infty}\frac{1}{2^i}+\dotsb - (1+\frac{1}{2}+\frac{1}{4}+\dotsb) \\ =2+1+\frac{1}{2}+\dotsb-2 \\ =2\times 2-2=2 \]
定义同上,只不过把\(2\)换成了\(k\),但没法套用上面的解法求解。这里仍然假定\(p\)是奇质数。
发现构造走不通了!不要紧,咱们学习了原根!
众所周知原根遍历了全部与\(p\)互质的数,因此咱们用原根替换它们。
好比说\(x^k \equiv a \pmod p\),设\(g\)为原根,\(a=g^s\),\(x=g^m\),则原式变成了\((g^m)^k \equiv g^s \pmod p\),也就是\(g^{km} \equiv g^s \pmod p\),\(s\)能够经过\(\text{BSGS}\)解得;\(m\)未知。而后由阶的性质获得\(km \equiv s \pmod{\varphi(p)}\),也就是\(km \equiv s \pmod{p-1}\),解\(m\)便可。
想要全部解?刚刚的同余方程的解在模\(p-1\)的意义下的全部取值即为全部解。事实上题目通常要求找出任意解,正常解同余方程便可。若是上面的任意一步都出了问题(好比说\(s\)无解,或者同余方程没有解),那就没有解了。
// 码量惊人,博主写了140+行。这里只放最核心部分 // 这里用K次剩余解决二次剩余问题 int solve(int k, int a, int P) { // x^k=a (%p) int s = BSGS(g, a, P); // 找出s使得g^s=a int d = gcd(k, P-1); if (s == -1 || s % d) return -1; // BSGS无解或同余方程无解,说明该问题无解 return qpow(g, 1ll*(s/d)*inv(k/d, (P-1)/d)%P, P); // 解同余方程km=s(%phi(p)) } int main() { init(); // 初始化质数表 T = read(); while (T--) { N = read(), P = read(); N %= P; if (!N) { printf("0\n"); continue; } if (P == 2) { printf("1\n"); continue; } getG(P); // 获得模P的原根 int ans = solve(2, N, P); // 解决K次剩余就写成solve(K, N, P) if (ans == -1) printf("Hola!\n"); else printf("%d %d\n", min(ans, P-ans), max(ans, P-ans)); } return 0; }
若是咱们想要快速判断一个数是否是质数,你可能会想到\(\text{O}(\sqrt n)\)复杂度的算法,但这个算法太劣了;或者想到运用费马小定理,由于当\(p\)为质数时有\(a^{p-1} \equiv 1 \pmod p\),因此猜想逆命题也成立。咱们经过随机\(a\)判断式子是否成立来判断\(p\)是否是质数。若是不许确,一次中了不表明必定是质数,那么咱们多测几回,能够将其为合数的几率降到很低。但有一类特殊的合数能让式子恒成立,不管你怎么选择\(a\),该同余式子一直成立,这类合数叫作\(\text{Carmichael}\)数,例如\(561\)。
那怎么办呢?先介绍一下二次探测原理。
若\(p\)是质数,且有\(a^2 \equiv 1 \pmod p\),那么\(a \equiv \pm 1 \pmod p\)。前文有相似的证实,也就是移项化成整除式,这里就不证了。
咱们经过这个方法来验证。
把\(p-1\)分解成\(2^k \times t\)。而后随机一个\(a\)而后让其自乘成\(a^2\),结合二次探测原理来判断,若是在某一时刻自乘出\(1\),则前一次必须是\(1\)或\(p-1\),不然违背了原理,这个必定是合数。测试\(t\)轮后就获得了\(a^{p-1}=a^{2^k \times t}\)。若是最后获得的数不是\(1\),那么就违背了费马小定理,\(p\)是合数。
听说测试\(n\)次的正确率为\(1-\left( \frac{1}{4} \right)^n\)。
int test[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // 听说用这几个数在OI的ll范围内出错率为0% int MR(ll P) { if (P == 1) return 0; // 毒瘤的1:他不是质数,但会卡后面 ll t = P-1; int k = 0; while (!(t & 1)) k++, t >>= 1; // 分解出2 rep(i, 0, 9) { if (P == test[i]) return 1; // test中的数都是质数。若是这个数出现过那它必定是质数 else if (test[i] > P) return 0; // 测试的数比模数大,说明测试失败了 ll a = pow(test[i], t, P), nxt = a; // 初始化a=test[i]^t%P rep(j, 1, k) { nxt = Mult(a, a, P); // 计算nxt=a^2%P。因为模数可能很大,这里的Mult用的是gui速乘 if (nxt == 1 && a != P-1 && a != 1) return 0; // 若是违背了二次探测定理,测试失败 a = nxt; } if (a != 1) return 0; // 违背了费马小定理,测试失败 } return 1; // 经过了全部的测试 }
忽略快(gui)速乘的复杂度,该复杂度为\(\text{O}(\log P)\)。
\(\text{Pollard-Rho}\)是一个用来快速分解质因数的算法,它巧妙的运用了“生日悖论”这一现象将\(N\)分解,配合着\(\text{Miller-Rabin}\)能够将复杂度降为\(\text{O}(N^{\frac{1}{4}})\)。
描述这个算法,咱们先从“生日悖论”提及。
班级里有\(50\)名学生,保证生日是随机的,不存在暗箱操做。假定一年为\(365\)天,问出现两我的生日相同的几率是多少?你可能脱口而出:\(\frac{50}{365}\)。
但事实上几率很是之高以致于几乎确定有两我的的生日在同一天,这个几率\(P=1-\frac{365 \choose 50}{365^{50}} \approx 97.142\%\)。为啥直觉跟计算出来的差别如此之大?缘由在于要保证两两生日都不等的几率占样本空间的比重很是之小。换个问题:若是你走进一个\(50\)人的班级里,你与班级里同窗生日相等的几率又是多少呢?这时就是\(\frac{50}{365}\)了。
而后有一个结论:出现两我的生日在同一天的指望人数是\(\text{O}(\sqrt N)\)。这个证实博主不会,直接跳过。
进入到\(\text{Pollard-Rho}\)前,还要介绍一个算法中的一个随机函数。
设随机函数\(f(x)\),在模\(P\)的意义下,知足\(f(x)=x^2+c\),\(c\)为随机参数,那么构成了\([0,P)\rightarrow[0, P)\)下的映射,且有\(f(x)=f(x+P)\)。借助初始参数\(x_0\)构造随机数列\(\{ x_i\}\),知足:\(\forall i>0 : x_i=f(x_{i-1})\)。显然其在模\(N\)意义下一定会走向一个循环,由于定义域和值域大小有限。这个函数生成的数列仍是很随机的,而后根据根据生日悖论有指望环大小为\(\text{O}(\sqrt P)\),缘由是若是出现了以前出现过的数字,显然就会走入循环,根据上面的结论,出现两个相同数字的指望次数就为\(\text{O}(\sqrt P)\)。把\(x\)的一步一步映射画出来图,发现长得十分像\(\rho\),算法由此得名。
对于将要被分解的数\(N\),咱们选取两个数字\(s\)和\(t\),若是有\((\mid s-t \mid, N) \neq 1,N\),很显然咱们找到了一个因子,能够递归分解两部分了。可是毕竟数字太多,如何合理地随机\(s\)和\(t\)呢?就运用随机函数。
若是咱们随机选取一些数字两两判断发现时间复杂度和空间复杂度又会弹回去。因此咱们采用“\(\text{Floyd}\)判圈法”:有两我的在起点\(x_0\)处,在环上一我的一次走一步,另外一个一次走两步。当他们再次相遇的时候代表一我的至少走完了一圈。这时一我的的数字记为\(s\),另外一个记为\(t\),再继续如上判断。运用此方法能更好判环。
指望下咱们须要\(\text{O}(N^{\frac{1}{4}})\)找出来一个因子。假设\(P\)可被分解,有\(P=a_1a_2,a_1 \leq a_2\),在模\(P\)意义下,生成出来的序列出现环的指望为\(\text{O}(\sqrt N)\)。对于环上的任意两个数\(x'\)和\(x''\),\((\mid x'-x'' \mid, P)=a_1\),当且仅当:\(x' \equiv x'' \pmod {a_1}\)成立。而生成的数列\(\{x_i\}\)在模\(N\)下是随机的,在模\(a_1\)下也是随机的,再次运用结论获得出现\(x'\equiv x''\)的指望为\(\text{O}(\sqrt{a_1})\),又由于有\(a_1 \leq \sqrt P\),因此获得指望为\(\text{O}(N^{\frac{1}{4}})\)。
可是还有种状况:\(x'=x''\),会致使\((\mid x' - x'' \mid, P)=P\)。这代表两我的相遇,选取的是环上的同一个数。遇到这种状况代表咱们随机数选取的很差,要更换随机参数\(c\)从新操做。至此最原始的\(\text{Pollard-Rho}\)就出来了。
分析复杂度时咱们只关心最大的数分解的复杂度,分解后的数字继续分解对总复杂度的贡献不大。但要不断求\(\gcd\),总复杂度为\(\text{O}(N^{\frac{1}{4}}\log N)\)。有\(\log\)的复杂度很差看,考虑削掉。
为了减小\(\gcd\)的次数,咱们将若干次获得的\(\mid s-t \mid\)的结果乘起来。\((a,b)=d\)时,\((ac \bmod b,b)\geq d\)必定成立。记乘积模\(N\)的值为\(prod\),若是\((prod,N)=1\),显然每一次\(\mid s-t \mid\)都与\(N\)互质;若是\(=N\),代表其中一定有因子或\(0\);不然就是\(N\)的因子。此时咱们退回来一步一步判断,找出因子或者是发现相遇了。调整适当的次数连乘,均摊下来复杂度就变成了\(\text{O}(N^{\frac{1}{4}})\)。
// 下列程序段实现求N的最大质因子 inline ll f(ll x, ll c, ll p) { return inc(Mult(x, x, p), c, p); } // 随机函数 ll Floyd(ll N) { ll c = rand()&15, s = 1, t = f(s, c, N); // 随机c,s=1表示x0=1,t表示从起点跳一步 for (;;) { ll _s = s, _t = t, prod = 1; for (int i = 1; i <= 128; i++) prod = Mult(prod, abs(_s - _t), N), _s = f(_s, c, N), _t = f(f(_t, c, N), c, N); // 连续处理128步 ll d = gcd(prod, N); // d=(prod, N) if (d == 1) { s = _s, t = _t; continue; } // 若是d=1,表示 for (int i = 1; i <= 128; i++) { if ((d = gcd(abs(s - t), N)) != 1) return d; // !=1返回 s = f(s, c, N), t = f(f(t, c, N), c, N); // 不然继续 } } } ll PR(ll N) { // Pollard-Rho主过程 if (MR(N)) return N; // 若是N是质数,直接返回 ll ans; while ((ans = Floyd(N)) == N); // 找到的是同一个点,从新来 return max(PR(ans), PR(N / ans)); // 递归,返回最大值 } int main() { srand(time(0)); scanf("%d", &T); while (T--) { scanf("%lld", &N); ll ans = PR(N); // 找最大的质因子 if (ans == N) printf("Prime\n"); else printf("%lld\n", ans); } return 0; }
博主曾经写过一篇,可能不够完整,在这里就放个连接了:Mobius反演学习