数论基础学习笔记
以前也也学了不少,整理一下。
1. 数学概念
1.1 模意义下的逆元
即 \(ax\equiv 1\pmod p\) 的解。
该同余方程有解当且仅当 $ a \perp p$。
对于 \(p\) 为质数,显然有 \(a^{p-2} \equiv a^{-1}\pmod p\)。
在2.1中将讲如何求解。
1.2 费马小定理与欧拉定理
1.2.1 费马小定理
无论学什么应该都是最先知道的定理: )。
费马小定理: 任意 \(p\) 为质数,且 \(a\perp p\),则有 \(a^{p-1} \equiv 1\pmod p\)。
引理:对于一个模 \(p\) 的完全剩余系 \(r\),若有 \(a\in\mathbb Z\),且 \(p\perp a\),则 \(r_i\times a(i\in [0,n-1])\) 也为模 \(p\) 的一个完系。
- 证明:反证,假设存在 \(i \not = j\) 使得 \(r_i \times a \equiv r_j\times a\pmod p\),因为 \(a \perp p\) 即存在 \(a\) 的逆元 \(a^{-1}\),既得 \(r_i\equiv r_j\pmod p\),矛盾,得证。
首先我们知道有一组完系 \(r\),去掉 \(0\) 为 \(r^{+}\),共有 \(p-1\) 个元素,若有 \(a\perp p\),则 \((r_1\times a)(r_2\times a)...(a_{p-1}\times a) \equiv r_1\times r_2\times...\times r_{p-1}\pmod p\),即得 \(a^{p-1} \equiv 1\pmod p\)。
1.2.2 欧拉定理
欧拉定理: 任意 \(p\in\mathbb{Z}\),且 \(a\perp p\),则有 \(a^{\varphi(p)} \equiv 1\pmod p\)。
证明类似。
引理:对于一个模 \(p\) 的简化剩余系 \(r\),若有 \(a\in\mathbb Z\),且 \(p\perp a\),则 \(r_i\times a(i\in [0,n-1])\) 也为模 \(p\) 的一个缩系。
- 首先这 \(\varphi(p)\) 个数彼此不同余,可由 1.2.1 中证出,下证 \(r_i\times a\) 与 \(p\) 互质,因为 \(r_i\perp p\),且 \(a \perp p\) 则 \(r_i \times a \bmod p\) 也互质,得证。
一个缩系 \(r\),\(a\perp p\),可以得出 \(r_1 r_2...r_{\varphi(p)} \equiv (a r_1)...(a r_{\varphi(p)}) \equiv a^{\varphi(p)}r_1...r_{\varphi(p)}\pmod p\),即得 \(a^{\varphi(p)} \equiv 1\pmod p\)。
当 \(p\) 为质数,则 \(\varphi(p) = p-1\) ,即得费马小定理。
1.3 阶与原根
不要问为什么先写原根,问就是学NTT
1.3.1 阶
阶的定义:使得 \(a^x \equiv 1\pmod m\) 的最小正整数 \(x\) 被称为 \(a\) 膜 \(m\) 的阶,记作 \(\delta_m(a)\)。
\(a \perp m\) 是 \(\delta_m(a)\) 存在的充要条件。
性质 1: \(a^x \equiv 1 \pmod m\) 当且仅当 \(\delta_m(a)|x\)。
性质 2:任意 \(i\in[1,\delta_m(a)]\),\(a^i\) 模 \(m\) 两两不同余。
- 证明:考虑反证,我们假设存在两个数 \(i,j\in[1,\delta_m(a)]\),使得 \(a^i\equiv a^j\pmod m\),则有 \(a^{|i-j|}\equiv 1\pmod m\),显然 \(|i-j| < \delta_m(a)\),这与阶的最小性矛盾,得证。
性质 3:\(\delta_m(a^k) = \dfrac {\delta_m(a)} {gcd(\delta_m(a),k)} = \dfrac {lcm(\delta_m(a),k)} k\) 具体不会证,可以看初等数论上的充要条件。
- 注意到 \((a^k)^x = a^{kx}\),因此 \(\delta_m(a)|kx\),得到 \(x_{\min} = \dfrac {\delta_m(a)} {gcd(\delta_m(a),k)}\)。
利用BSGS可求阶,复杂度 \(O(\sqrt{p})\)。
1.3.2 原根
定义:对于 \(m \in\mathbb{N}_{+},a\in\mathbb{Z}\) 且 \(a\perp m\),若 \(\delta_m(a) = \varphi(m)\),则称 \(a\) 为模 \(m\) 的原根。
原根判定定理:对于 \(m \geq 3,a\perp m\),\(a\) 是模 \(m\) 的原根的充要条件是对于任意 \(\varphi(m)\) 的质因子 \(p\),均有 \(a^{\frac {\varphi(m)} p} \not\equiv 1\pmod m\)。
- 证明:因为 \(\dfrac {\varphi(m)} p\) 的所有质因子取遍了 \(\varphi(m)\) 的真因数,所以若存在 \(a^d\equiv 1\pmod m\),使得 \(d < \varphi(m)\),则必然存在 \(\varphi(m)\) 的质因子 \(p\) 使得 \(d|\dfrac {\varphi(m)} p\),这说明 \(a^{\frac {\varphi(m)} p} \equiv 1\pmod m\),矛盾。
原根存在定理:一个数 \(m\) 存在原根的充要条件是 \(m = 2,4,p^a,2p^a\)(\(p\) 是奇质数)。
证明详见初等数论(其实没必要)
原根个数:一个数 \(m\) 有原根,则其原根的个数为 \(\varphi(\varphi(m))\)。
- 证明:由原根的性质知:$$\delta_m(g^k) = \dfrac {\delta_m(a)} {gcd(\delta_m(a),k)} = \dfrac {\varphi(m)}{gcd(\varphi(m),k)}$$
则若 \(\varphi(m) \perp k\),则有 \(\delta_m(g^k) = \varphi(m)\),即 \(g^k\) 也是模 \(m\) 的原根,而满足 \(\varphi(m) \perp k\) 的 \(k\) 的个数为 \(\varphi(\varphi(m))\),即原根有 \(\varphi(\varphi(m))\) 个。
中国数学家王元证明了最小原根的级别为 \(O(n^{0.25+\epsilon})\)。
所以我们可以直接枚举,复杂度 \(O(n^{0.25}log(n))\)。
进而求出所有原根。
2. 扩展欧几里得算法
也叫ExGCD
即求方程 \(ax + by = c\) 的解,当且仅当 \(gcd(a,b)|c\) 有无穷多项解。
我们考虑欧几里得法的最后一步,即 \(b = 0\) 时,一定有解 \(x = 1,y = 0\) ,我们可以归纳求解,假设已知 \(gcd(b,a\bmod b) = x_0b + (a\bmod b)y_0\),则我们直接拆开 \(gcd(a,b) = x_0b + (a\bmod b)y_0 = bx_0 + ay_0 - b\left\lfloor\dfrac a b\right\rfloor y_0 = ay_0 + b(x_0 - \left\lfloor\dfrac a b\right\rfloor y_0)\),可得一解 \(x = y_0,y = x_0 - \left\lfloor\dfrac a b\right\rfloor y_0\),复杂度同欧几里得,\(O(logn)\),最后将 \(x,y\) 乘上 \(\dfrac c {gcd(a,b)}\) 即可。
我们令一组特解为 \(x_0,y_0\)
通解即为 \(x = x_0 + \dfrac b {gcd(a,b)}k,y = y_0 - \dfrac a {gcd(a,b)}k(k\in \mathbb{Z})\)
int t;
ll a,b,c,gc,x,y;
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0){return x = 1,y = 0,void();}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
int main(){
t = read();
while(t--){
a = read(),b = read(),c = read();
gc = __gcd(a,b);
ll dx = b/gc,dy = a/gc;
if(c % gc != 0){//无解
printf("-1\n");
continue;
}
exgcd(a,b,x,y);
x = x * c / gc,y = y * c / gc;//特解
ll l = ceil(double(-x+1)/dx),r = floor((double)(y-1)/dy);// > 0 的区间
if(l > r)printf("%lld %lld\n",x + dx*l,y - dy*r);
else printf("%lld %lld %lld %lld %lld\n",r-l+1,x + dx*l,y - dy*r,x + dx*r,y - dy*l);
}
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
线性同余方程: \(ax \equiv c\pmod p\)
可以转化为求解不定方程 \(ax + kp = c\),同余方程有解当且仅当 \(gcd(a,p)|c\),解集是 \(x = x_0 + \dfrac p {gcd(a,p)}k(k\in\mathbb{Z})\)。
求乘法逆元当 \(p\) 不为质数时可用。
3. 扩展中国剩余定理(ExCRT)
不讲中国剩余定理。
求解 \(n\) 个线性同余方程组 \(x \equiv a_i\pmod {b_i}(a_i < b_i)\) 的最小解 \(x\)。
首先第一个同余方程的一个特解显然是 \(a_1\),我们考虑已经知道前 \(i\) 个同余方程组的解如何求出前 \(i+1\) 个同余方程组的解。
我们假设前 \(i\) 个同余方程组的解为 \(x_0\),\(M = lcm(a_1,a_2,...a_i)\),则显然前 \(i\) 个同余方程组的解集为 \(x_0 + kM(k\in\mathbb{Z})\),则我们只需考虑同余方程 \(x_0 + kM \equiv a_{i+1}\pmod {b_{i+1}}\),移项得 \(kM \equiv a_{i+1}-x\pmod {b_{i+1}}\),可以用扩展欧几里得算法求出特解 \(k_0\),得出 \(k\) 的解集 \(k = k_0 + \dfrac {b_{i+1}} {gcd(b_{i+1},M)}\omega(\omega\in\mathbb{Z})\),求得最小 \(k_{min}\),最后得出前 \(i+1\) 方程组最小解\(x_0 = x_0 + k_{min}\times M\)。
当有一次扩欧无解时整个同余方程无解。
会爆 \(long long\),需要龟速乘。
int n;
ll a[N],b[N];
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0){return x = 1,y = 0,void();}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
ll mul(ll x,ll y,ll p){return (x*y-(ll)(x/(long double)p*y+1e-3)*p+p) % p;}
ll lcm(ll x,ll y){return x / __gcd(x,y) * y;}
ll EXCRT(){
ll x = a[1],M = b[1],k = 0,y = 0;
F(i,2,n){
ll gc = __gcd(M,b[i]);
if((a[i]-x) % gc != 0)return -1;//无解
exgcd(M,b[i],k,y);
ll mod = b[i] / gc;
k = (mul(k,(a[i] - x) / gc,mod) + mod) % mod;
x = x + k * M;
M = lcm(M,b[i]);
}//EXCRT
return x;
}
int main(){
n = read();
F(i,1,n)b[i] = read(),a[i] = read();
printf("%lld\n",EXCRT());
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4. 离散对数问题
求解同余方程 \(a^x \equiv b\pmod p\) 的解集。
该问题还未有 \(O(polylog(n))\) 求法。
4.1 BSGS(大步小步算法)
当 \(a \perp p\) 时,可以 \(O(\sqrt{p})\) 求解。
我们令 \(x = i \ast t - j(t = \lceil\sqrt{p}\rceil,j\in[0,t-1])\)。
则方程变为 $ a^{i\ast t-j} \equiv b\pmod p$,即 \(({a^t})^i \equiv b\ast a^j\pmod p\)。
我们枚举所有的 \(j\in[0,t-1]\),将 \(b \ast a^j \bmod p\)插入到一个 \(Hash\) 表内。
然后我们再枚举所有的 \(i\in[0,t]\),计算 \(({a^t})^i \bmod p\),在 \(Hash\) 表内查找是否存在对应的 \(j\)。
复杂度 \(O(\sqrt{n})\)。
ll a,b,p;
ll ksm(ll x,ll y,ll mod){
ll ans = 1;
while(y){
if(y & 1)ans = (ans * x) % mod;
x = (x * x) % mod,y >>= 1;
}return ans;
}
ll BSGS(ll a,ll b,ll p){
map<int,int>mp;mp.clear();
b %= p;ll t = sqrt(p)+1;
for(int j = 0;j < t;j++,b = b * a % p)mp[b] = j;
ll aa = ksm(a,t,p);a = 1;
for(int i = 0;i <= t;i++,a = a * aa % p)
if(mp.find(a) != mp.end() && i * t - mp[a] >= 0)return i * t - mp[a];
return -1;
}
int main(){
p = read(),a = read(),b = read();
ll ans = BSGS(a,b,p);
if(ans != -1)printf("%lld\n",ans);
else printf("no solution\n");
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4.2 ExBSGS
考虑一般情况,我们尽量把问题转化为 $ a \perp p$ 的情况,我们设 \(d = gcd(a,p)\),我们将等式同除 \(d\),得到 \(a^{x-1}\dfrac a d\equiv\dfrac b d\pmod{\dfrac p d}\),若 \(b\nmid d\) 则无解。
可以发现 \(\dfrac a d \perp\dfrac p d\),即存在逆元,需要Exgcd求出,得到 \(a^{x-1}\equiv\dfrac b d(\dfrac a d)^{-1}\pmod{\dfrac p d}\),我们重复上述操作直到 \(a \perp \dfrac p {d^k}\)。
最后式子为 \(a^{x-k} \equiv\dfrac b{d^k}(\dfrac a{d^k})^{-1}\pmod{\dfrac p {d^k}}\),我们可以BSGS求解。
复杂度 \(O(log^2p + \sqrt{p})\)(需要求逆元)。
ll a,b,p;
ll ksm(ll x,ll y,ll mod){
ll ans = 1;
while(y){
if(y & 1)ans = (ans * x) % mod;
x = (x * x) % mod,y >>= 1;
}return ans;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if(b == 0)return x = 1,y = 0,void();
exgcd(b,a%b,y,x);
y -= a / b * x;
}
ll inv(ll x,ll p){return exgcd(x,p,x,*new ll),(x % p + p) % p;}
ll BSGS(ll a,ll b,ll p){
map<int,int>mp;mp.clear();
b %= p;ll t = sqrt(p)+1;//x = i * t - j
for(int j = 0;j < t;j++,b = b * a % p)mp[b] = j;
ll aa = ksm(a,t,p);a = 1;
for(int i = 0;i <= t;i++,a = a * aa % p)
if(mp.find(a) != mp.end() && i * t - mp[a] >= 0)return i * t - mp[a];
return -1;
}
ll EXBSGS(ll a,ll b,ll p){
ll d = __gcd(a,p),k = 0;
b %= p,a %= p;
if(b == 1 || p == 1)return k;
while(d > 1){
if(b == 1)return k;
if(b % d)return -1;
p /= d;b = b / d * inv(a / d,p) % p;
d = __gcd(a % p,p),k++;
}
ll ans = BSGS(a,b,p);
return ans == -1 ? -1 : ans + k;
}
int main(){
while(1){
a = read(),p = read(),b = read();
if(!p)break;
ll ans = EXBSGS(a,b,p);
if(ans != -1)printf("%lld\n",ans);
else printf("No Solution\n");
}
return cerr<<endl<<"Time:"<<clock()<<"ms"<<endl,0;
}
4.3 习题
I P2485 [SDOI2011] 计算器
第一问快速幂,第二问扩欧,第三问BSGS。
首先我们求一下通项。
\(x_n = ax_{n-1} + b\),我们设 \(\lambda\) 与 \(A\) 使得 \(x_n + \lambda = A(x_{n-1} + \lambda)\)。
解得 \(A = a,\lambda = \dfrac b {a-1}\).
则 \(x_n = a(x_{n-1} + \dfrac b {a-1}) - \dfrac b {a-1} = a^{n-1}(x_1 + \dfrac b {a-1}) - \dfrac b {a-1}\)。
我们要求的及是:
这就很板了,BSGS即可(注意 \(a = 1,a = 0\) 的特判)。
5. 线性方程组
5.1 高斯消元法
求解 \(n\) 元一次方程组。
主要就是矩阵变换。
只需要知道将系数矩阵中的任意一行乘上一个常数加到另一行整个方程式不变的。
高斯消元就是把系数矩阵通过矩阵变换转化为对角矩阵进行求解。
高斯法:
高斯-约旦法:
int n;
db a[N][N],b[N];
void Gauss(){
for(int i = 1;i <= n;i++){
int k = i;
for(int j = i+1;j <= n;j++)
if(fabs(a[j][i]) > fabs(a[k][i]))k = j;
for(int j = 1;j <= n;j++)swap(a[i][j],a[k][j]);swap(b[i],b[k]);
if(fabs(a[i][i]) < 1e-8)return printf("No Solution\n"),void();
db c = a[i][i];
for(int j = 1;j <= n;j++)a[i][j] /= c;b[i] /= c;
for(int j = 1;j <= n;j++){
if(i == j)continue;
db c = a[j][i];
for(int k = i;k <= n;k++)a[j][k] -= c * a[i][k];
b[j] -= c * b[i];
}
}
for(int i = 1;i <= n;i++)printf("%.2lf\n",b[i]);
}
int main(){
n = read();
for(int i = 1;i <= n;i++){
for(int j = 1;j <= n;j++)a[i][j] = read();
b[i] = read();
}
Gauss() ;
return cerr << "Time : " << clock() << " ms" << endl, 0;
}
高斯消元只是一个解方程的方法。
5.2 习题
我们设每个每个点到球心的距离为 \(C\),球心坐标为 \((x_1,x_2,...,x_n)\) 则我们需要解方程:
这是一个 \(n+1\) 元二次方程,无法解决。
我们把相邻两项相减即可消掉二次项与常数项。
是 \(n\) 元一次方程组,高斯消元即可。
6. 卢卡斯定理
7. 二次剩余
难
参考文章:
魏老师:初等数论学习笔记 I:同余相关