文章目录

  • 一.质数
    • 1.定义
    • 2.质数的判断
    • 3.质数的筛选
    • 4.质因子分解
    • 5.互质
  • 二.同余
    • 1.模运算
    • 2.同余
    • 3.欧拉定理
    • 4.同余方程
    • 5.同余方程组
    • 6.原根
    • 7.高次同余方程

数论是数学的一个分支,研究的是整数的性质。本篇文章总结的是ACM中需要用到的数论知识、模板。

一.质数

1.定义

质数,又称素数,若一个正整数无法被除了1和它自身以外的其它数整除,则称其为质数,否则为合数。特殊地,1既不是合数也不是质数

2.质数的判断

给定一个数,判断是否为质数的方法:

试除法: 检查所有可能成为n的因子的数,若没有找到因子,则证明这个数是一个质数

一种朴素的做法就是从2遍历到N-1观察有无N的因子,若没有则说明N为质数,依据的想法是N的因子必然在1~N的范围内

改进: 其实我们只要找到一个N的因子(除了1和它本身)就可以证明N是个合数。

定理:如果N是一个合数,则必然存在一个N的因子T,满足 2 ≤ T ≤ √ N 2≤T≤√N 2≤T≤√N

证明:反证法即可,假设合数N的所有因子都大于 √ N √N √N,则任取一因子X,因为X为N的因子,所以N/X也是N的一个因子,而由于X大于 √ N √N √N,所以N/X必然小于 √ N √N √N,与假设相矛盾

根据这个定理,我们只需要从2遍历到√N即可,时间复杂度缩小为 O ( √ N ) O(√N) O(√N),若没找到因子则N为质数。

其它的想法:

素数筛进行预处理,用prime数组存放所有素数,然后从 p r i m e [ 1 ] prime[1] prime[1]遍历到 p r i m e [ i ] ∗ p r i m e [ i ] ≤ N prime[i]*prime[i]≤N prime[i]∗prime[i]≤N,因为N如果是合数,则必然存在小于等于√N的素数因子

证明:前文已经证明过必然存在小于等于√N的因子,如果这个因子是素数自不必说,如果是合数,那么合数可以被分解为素因子的乘积。

目的:可以进一步减少时间复杂度,需要遍历的数更少了(因为素数的分布相对稀疏,10万以内的素数只有9500多个)

当然这只是我个人的想法,没有仔细的验证与思考,并且大部分时候O(√N)的复杂度就已经很优秀了

3.质数的筛选

筛选:即从1~N中筛选出所有的质数

思路:一个数x的倍数——2x,3x,4x……必然不是素数

具体做法:从2开始扫描所有的数x,并将x的所有倍数标记为合数。如果扫描到一个数y,发现y没被标记过,说明y一定是素数,因为2~y-1没有y的因子,符合素数定义

因为每个合数必然有质因子,所以只让素数来进行标记的工作以减少重复标记,比如27让3来标记为合数即可。

这就是埃氏筛

缺点:有些数仍会被重复标记,比如12既是3的倍数又是2的倍数,具有多个质因子的数会被重复标记

改进:

线性筛/欧拉筛:

即找到一个数的唯一产生方式:让一个数的最小质因子对其进行标记,比如12,虽然有2,3两个质因子,但只让2来标记12。

时间复杂度接近 O ( N ) O(N) O(N),所以称为线性筛

实现:

int Prime[100005];    //存放所有的质数
bool Isprime[100005]; //判断一个数是否为质数
int cnt=1;void get_Prime(){for(int i=2;i<=100000;i++) Isprime[i]=1; //先默认所有数为质数for(int i=2;i<=100000;i++){if(Isprime[i]) Prime[cnt++]=i;for(int j=1;j<cnt&&Prime[j]*i<=100000;j++){ Isprime[Prime[j]*i]=0; //将其标记为合数if(i%Prime[j]==0)break;}}
}

对于if(i%Prime[j]==0) break; 的解释:

线性筛的思想是只让最小质因子对合数进行标记,也就是:Isprime[Prime[j]*i]=0; 这句话,这里Prime[j]即最小质因子。

如果i%Prime[j]==0 ,不妨将i表示为k*Prime[j],如果令j++继续筛下去的话,下一个要筛的数是Prime[j+1]*i,显然这个数的最小质因子应该是Prime[j]而不是Prime[j+1],违背了线性筛的思想,所以需要break

4.质因子分解

算术基本定理:

任何一个大于1的正整数都可以被分解为质因子的幂次的积

即 N = P 1 a 1 ⋅ P 2 a 2 ⋅ P 3 a 3 ⋅ ⋅ ⋅ P m a m N=P_1^{a_1}\cdot P_2^{a_2}\cdot P_3^{a_3}\cdot\cdot\cdot P_m^{a_m} N=P1a1​​⋅P2a2​​⋅P3a3​​⋅⋅⋅Pmam​​

这个定理看似非常简单,但是在很多地方都会应用到,要有将一个数分解为质因子幂次乘积的思想,他给我们提供了一种看待自然数的角度

分解质因子:

int factor[100];
int cnt=0;void divide(int N){cnt=0;for(int i=2;i*i<=N;i++){if(N%i==0){factor[cnt++]=i;while(N%i==0)N/=i;}}if(N>1)  factor[cnt++]=N; //质数在根号N内没有因子,故可能没除完
}

如果还想知道每个质因子的幂次再用一个数组存就行

5.互质

最大公约数:great common divisor,简称gcd。两个数的最大公约数即两个数共有的约数(因子)中值最大的数。

互质定义:若gcd(a,b)=1,则称a与b互质。(即不含共同因子)

常见的求gcd的方法为欧几里得算法,时间复杂度为 O ( l o g ( N ) ) O(log(N)) O(log(N))的级别

关于欧几里得算法求gcd的证明:欧几里得算法证明

欧拉函数:

在数论中,对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(因此φ(1)=1)。此函数以其首名研究者欧拉命名(Euler’s totient function),它又称为Euler’s totient function、φ函数、欧拉商数等。 例如φ(8)=4,因为1,3,5,7均和8互质。

基础可见:欧拉函数计算及打表

下面来说一下两个较特殊的欧拉函数值:

1) φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p−1 (p为质数)

2) φ ( p k ) = p k − p k − 1 φ(p^k)=p^k-p^{k-1} φ(pk)=pk−pk−1

[ 1 , p k ] [1,p^k] [1,pk]中,和 p k p^k pk有共同因子(不互质)的数有 p ∗ 1 , p ∗ 2 , p ∗ 3 ⋅ ⋅ ⋅ , p ∗ p k − 1 p*1,p*2,p*3\cdot\cdot\cdot ,p*p^{k-1} p∗1,p∗2,p∗3⋅⋅⋅,p∗pk−1,共计 p k − 1 p^{k-1} pk−1个

3) 若 g c d ( s , r ) = 1 , φ ( s ∗ r ) = φ ( s ) ∗ φ ( r ) 若gcd(s,r)=1,φ(s*r)=φ(s)*φ(r) 若gcd(s,r)=1,φ(s∗r)=φ(s)∗φ(r)

二.同余

1.模运算

对于任意的两个整数a、b(b>0),必然存在等式:

a = k b + r a=kb+r a=kb+r

其中k为a除以b的商,r为a除以b的余数。在C语言中,可以利用a/b得到k,利用a%b得到r。前者称为整除,后者即模运算。

模运算的实质其实就是a-kb

性质:

① ( a + b ) % p = ( a % p + b % p ) % p (a+b)\%p=(a\%p+b\%p)\%p (a+b)%p=(a%p+b%p)%p

② ( a − b ) % p = ( a % p − b % p + p ) % p (a-b)\%p=(a\%p-b\%p+p)\%p (a−b)%p=(a%p−b%p+p)%p(加p防止结果变为负数)

③ ( a ∗ b ) % p = ( a % p ) ∗ ( b % p ) % p (a*b)\%p=(a\%p)*(b\%p)\%p (a∗b)%p=(a%p)∗(b%p)%p

④除法不满足分配律,需要用到乘法逆元将除法转化为乘法:

逆元:

一个数和它的逆元运算后得到的是这个运算的单位元

比如加法的单位元是0(任何数加0都等于其本身)

一个数a的加法逆元就是-a

比如说乘法的单位元是1(任何数乘1都等于其本身)

一个数a的”乘法逆元“就是1/a,即其倒数

注意上面的这个乘法逆元也就是所谓的倒数,而下面要讲的乘法逆元指的是模意义下的乘法逆元

乘法逆元:

若 b ⋅ b − 1 ≡ 1 ( m o d p ) b\cdot b^{-1} ≡ 1(mod\ p) b⋅b−1≡1(mod p)

则称 b − 1 b^{-1} b−1为b在模p下的乘法逆元,可以理解为模p意义下的倒数

则 ( a / b ) % p = a ∗ b − 1 % p (a/b)\%p=a*b^{-1}\%p (a/b)%p=a∗b−1%p 将除法转化为了乘法

例:

由于 2 ⋅ 2 ≡ 1 ( m o d 3 ) 2\cdot 2 ≡ 1(mod \ 3) 2⋅2≡1(mod 3),所以2在模3意义下的乘法逆元就是它本身

8 / 2 ≡ 8 ⋅ 2 ≡ 1 ( m o d 3 ) 8/2≡8\cdot 2≡1(mod \ 3) 8/2≡8⋅2≡1(mod 3)

乘法逆元存在的前提是 g c d ( b , p ) = 1 gcd(b,p)=1 gcd(b,p)=1

在ACM中用到的模数p一般都是质数,所以只要b不为0或p的倍数即可(0和p的倍数在模p意义下不存在逆元)。

若p不是质数,则要求b、p互质,否则逆元不存在

逆元求法:

一个数在模p意义下的乘法逆元有很多种求法,这里写一下我常用的一种求逆元的方法:

费马小定理:

p为质数且gcd(b,p)=1,则 b p − 1 ≡ 1 ( m o d p ) b^{p-1}≡ 1(mod\ p) bp−1≡1(mod p)

也就是说 b p − 1 % p = 1 b^{p-1}\%p=1 bp−1%p=1

可以验证一下:

当p=3时, 2 2 , 4 2 , 5 2 , 7 2 2^2,4^2,5^2,7^2 22,42,52,72模p都等于1

而 b p − 1 = b p − 2 ⋅ b b^{p-1}=b^{p-2}\cdot b bp−1=bp−2⋅b

所以b的一个乘法逆元为 b p − 2 b^{p-2} bp−2,用快速幂即可较快求得

long long inv(long long x){return quickpow(x,mod-2)%mod;
}

⑤快速乘法

C自带的乘法运算很优秀,但是在计算 a ⋅ b % p a\cdot b \%p a⋅b%p时,如果p很大,即使使用分配律还是可能会超过数据类型上限,所以可以使用快速乘法来避免这个问题,不会快速乘的可以看这篇↓:

快速幂+快速乘法

2.同余

若整数a和整数b除以正整数m的余数相等(即 a % m = b % m a\% m=b\%m a%m=b%m),则称a,b模m同余,记为 a ≡ b ( m o d m ) a≡b(mod\ m) a≡b(mod m)

下面的只要大概知道个概念就好

同余类:

亦称剩余类,为数论的基本概念之一。设模为m,则根据余数可将所有的整数分为m类:

把所有与整数a模m同余的整数构成的集合叫做模m的一个剩余类,记作[a]。(也见过a上面加一杠的写法)并把a叫作剩余类[a]的一个代表元。

例:在模5意义下,3、8、13都属于同余类[3]

简化剩余系:

同余类的集合构成了m的完全剩余系

简化剩余系指m的完全剩余系中与m互质的数的集合

简化剩余系关于模m乘法封闭,设a、b为简化剩余系中任意两个数,则a×b必然也属于简化剩余系

同余的性质:

介绍在下面要用到的一些同余的性质

①若 a ≡ b ( m o d n ) a≡b(mod\ n) a≡b(mod n) 则 a ∗ c ≡ b ∗ c ( m o d n ) a*c≡b*c(mod\ n) a∗c≡b∗c(mod n)

②若 a ≡ b ( m o d n ) a≡b(mod \ n) a≡b(mod n) 则 a q ≡ b q ( m o d n ) a^q≡b^q(mod\ n) aq≡bq(mod n)

③若 a ≡ b ( m o d n ) , c ≡ d ( m o d n ) a≡b(mod\ n), c≡d(mod\ n) a≡b(mod n),c≡d(mod n),则 a ∗ c ≡ b ∗ d ( m o d n ) a*c≡b*d(mod\ n) a∗c≡b∗d(mod n)

由乘法对于模运算的分配律可以证明↑

( a ∗ c ) % n = ( a % n ) ∗ ( c % n ) % n = ( b % n ) ∗ ( d % n ) % n = ( b ∗ d ) % n (a*c)\%n=(a\%n)*(c\%n)\%n=(b\%n)*(d\%n)\%n=(b*d)\%n (a∗c)%n=(a%n)∗(c%n)%n=(b%n)∗(d%n)%n=(b∗d)%n

①②可由③推得

————————————————————————

④若 a ≡ b ( m o d n ) 且 k > 0 a≡b(mod \ n)且k>0 a≡b(mod n)且k>0, 则 a k ≡ b k ( m o d n k ) ak≡bk(mod\ nk) ak≡bk(mod nk)

证明:令 a = t n + b a=tn+b a=tn+b,则 k a = t k n + k b ka=tkn+kb ka=tkn+kb,或者写为 k a − k b = t ∗ k n ka-kb=t*kn ka−kb=t∗kn,证毕

⑤若 a ≡ b ( m o d n ) 且 d ∣ a , d ∣ b , d ∣ n a≡b(mod \ n)且d|a,d|b,d|n a≡b(mod n)且d∣a,d∣b,d∣n, 则 a d ≡ b d ( m o d n d ) \frac{a}{d}≡\frac{b}{d}(mod\ \frac{n}{d}) da​≡db​(mod dn​)

证明同上

其他性质可参见百度百科

3.欧拉定理

若正整数a,n互质,则 a φ ( n ) ≡ 1 ( m o d n ) a^{φ(n)}≡1(mod \ n) aφ(n)≡1(mod n)

证明不会

这个定理很重要,会在之后讲原根时再深入讲解

费马小定理其实就是欧拉定理在n为素数时的特殊情况

推论:

若a,n互质,b为任意正整数,则 a b ≡ a b m o d φ ( n ) ( m o d n ) a^b≡a^{b\ mod \ φ(n)}(mod \ n) ab≡ab mod φ(n)(mod n)

——————————————————————————

证明:

令 b = q ∗ φ ( n ) + r b=q*φ(n)+r b=q∗φ(n)+r,则:

a b ≡ a q ∗ φ ( n ) + r ≡ ( a φ ( n ) ) q ∗ a r ≡ 1 q ∗ a r ≡ a r ≡ a b m o d φ ( n ) ( m o d n ) a^b≡a^{q*φ(n)+r}≡(a^{φ(n)})^{q}*a^r≡1^q*a^r≡a^r≡a^{b\ mod \ φ(n)}(mod \ n) ab≡aq∗φ(n)+r≡(aφ(n))q∗ar≡1q∗ar≡ar≡ab mod φ(n)(mod n)

——————————————————————————

应用:

前面已经说明了,加减乘除的模运算处理方式,这个推论可以以应用于幂次运算的模运算处理方式

a b % p = ( a % p ) b % φ ( p ) a^b\%p=(a\%p)^{b\%φ(p)} ab%p=(a%p)b%φ(p)

如果这个 p p p是质数的话,就比较方便了,因为 φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p−1,否则还要求一个欧拉函数值 O ( √ P ) O(√P) O(√P)的复杂度

当然,一般来说快速幂 l o g ( N ) log(N) log(N)级别的复杂度已经足够优秀

引理:

a,n互质,满足 a x ≡ 1 ( m o d n ) a^x≡1 (mod \ n) ax≡1(mod n)的最小正整数x一定是 φ ( n ) φ(n) φ(n)的约数

反证法:

假设满足条件的最小正整数 x 0 x_0 x0​不是 φ ( n ) φ(n) φ(n)的约数

首先 a φ ( n ) ≡ 1 ( m o d n ) a^{φ(n)}≡1(mod \ n) aφ(n)≡1(mod n)是一定成立的,所以 x 0 < φ ( n ) x_0<φ(n) x0​<φ(n)

令 φ ( n ) = q ∗ x 0 + r ( 0 < r < x ) φ(n)=q*x_0+r\ (0<r<x) φ(n)=q∗x0​+r (0<r<x)

因为 a x 0 ≡ 1 ( m o d n ) a^{x_0}≡1(mod\ n) ax0​≡1(mod n)

所以 a q ∗ x 0 ≡ ( a x 0 ) q ≡ 1 ( m o d n ) a^{q*x_0}≡(a^{x_0})^q≡1(mod \ n) aq∗x0​≡(ax0​)q≡1(mod n)

因为 a φ ( n ) ≡ a q ∗ x 0 + r ≡ a a ∗ x 0 ∗ a r ≡ 1 ( m o d n ) a^{φ(n)}≡a^{q*x_0+r}≡a^{a*x_0}*a^r≡1(mod \ n) aφ(n)≡aq∗x0​+r≡aa∗x0​∗ar≡1(mod n)

所以 a r ≡ 1 ( m o d n ) a^r≡1(mod\ n) ar≡1(mod n)

因为r< x 0 x_0 x0​ 所以假设不成立

4.同余方程

贝祖定理:

对任意整数a、b,存在x、y满足不定方程 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

证明:

(这个证明比较繁琐,可以不看,记住结论就好)

欧几里得算法求gcd(a,b)时:

long long  gcd(long long  a,long long  b){return a%b==0?b:gcd(b,a%b);
}

递归函数的终点是a%b==0,此时gcd(a,b)==b, a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)明显是有解的,易得x=0,y=1为一组特解

运用数学归纳法,只要证明:

b ∗ x + ( a % b ) ∗ y = g c d ( b , a % b ) b*x+(a \% b)*y=gcd(b, a\%b) b∗x+(a%b)∗y=gcd(b,a%b)有解,则 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)必有解

那么贝祖定理就成立了

a % b = a − b ∗ [ a / b ] ( [ a / b ] 表 示 整 除 ) a\%b=a-b*[a/b]\ ([a/b]表示整除) a%b=a−b∗[a/b] ([a/b]表示整除)

b ∗ x + ( a % b ) ∗ y b*x+(a \% b)*y b∗x+(a%b)∗y

= = b ∗ x + ( a − b ∗ [ a / b ] ) ∗ y ==b*x+(a-b*[a/b])*y ==b∗x+(a−b∗[a/b])∗y

= = a ∗ y + b ∗ ( x − [ a / b ] y ) ==a*y+b*(x-[a/b]y) ==a∗y+b∗(x−[a/b]y)

= = g c d ( b . a % b ) ==gcd(b.a\%b) ==gcd(b.a%b)

= = g c d ( a , b ) ==gcd(a,b) ==gcd(a,b)

所以令 x ′ = y , y ′ = x − [ a / b ] y x^{'}=y,y^{'}=x-[a/b]y x′=y,y′=x−[a/b]y,则 a x ′ + b y ′ = g c d ( a , b ) ax^{'}+by^{'}=gcd(a,b) ax′+by′=gcd(a,b)

综上,贝祖定理成立

扩展欧几里得:

由上述的思路,可以写出扩展欧几里得的算法

代码如下:

(代码不长,可以选择背模板)

long long  exgcd(long long  a,long long  b,long long  &x,long long &y){if(a%b==0) {x=0;y=1;return b;}long long  d=exgcd(b,a%b,x,y);long long temp=x; x=y;y=temp-(a/b)*y;return d;
}

应用:

①解不定方程ax+by=gcd(a,b)

函数返回的两个值x,y就是不定方程的一组特解

对于更一般化的方程ax+by=c,它有解当且仅当 c % g c d ( a , b ) = = 0 c\%gcd(a,b)==0 c%gcd(a,b)==0

我们可以先解出ax+by=gcd(a,b)的一组特解,然后令其同时乘上 c g c d ( a , b ) \frac{c}{gcd(a,b)} gcd(a,b)c​即得方程ax+by=c的特解 x 0 , y 0 x_0,y_0 x0​,y0​

方程ax+by=c的通解可以表示为

x = x 0 + k ⋅ b g c d ( a , b ) x=x_0+k\cdot \frac{b}{gcd(a,b)} x=x0​+k⋅gcd(a,b)b​

y = y 0 − k ⋅ a g c d ( a , b ) y=y_0-k\cdot \frac{a}{gcd(a,b)} y=y0​−k⋅gcd(a,b)a​

求x的最小正整数解:

令 t e m p = b g c d ( a , b ) 令temp= \frac{b}{gcd(a,b)} 令temp=gcd(a,b)b​

则最小正整数解为 ( x 0 % t e m p + t e m p ) % t e m p (x_0\%temp+temp)\%temp (x0​%temp+temp)%temp

更正:此处并不是最小正整数解,而是最小非负整数解,在答案可能为0的时候需要特判(即if(x==0) x=temp;

求y的最小正整数解:

则令 t e m p = a g c d ( a , b ) temp=\frac{a}{gcd(a,b)} temp=gcd(a,b)a​即可

②解线性同余方程

扩欧可以解决这种形式的同余方程:ax≡c(mod b) (1<c<b)

该方程显而易见可以转化为不定方程ax+by=c

③求逆元:

逆元的定义 a ∗ a − 1 ≡ 1 ( m o d p ) a*a^{-1}≡1(mod \ p) a∗a−1≡1(mod p)

将逆元 a − 1 a^{-1} a−1表示为x

则可以转化为不定方程 a ∗ x + p ∗ y = 1 a*x+p*y=1 a∗x+p∗y=1

该方法相比于小费马定理略显麻烦,但是应用范围更广,只要求a、p互质即可,而小费马定理要求p为质数

5.同余方程组

形如a*x≡b (mod m)的方程,因为未知数x的指数为1,所以称为一次同余方程或线性同余方程

该方程要么无解,要么有无数个解

可以使用扩展欧几里得算法求解这类方程

中国剩余定理:

中国剩余定理(孙子定理)可以用于解决如下形式的线性同余方程组(一元线性同余方程组):


其中 m 1 , m 2 ⋅ ⋅ ⋅ , m n m_1,m_2\cdot\cdot\cdot,m_n m1​,m2​⋅⋅⋅,mn​是两两互质的整数

令 m = ∏ i = 1 n m i , M i = m / m i m=∏_{i=1}^nmi \ ,\ \ M_i=m/mi m=∏i=1n​mi ,  Mi​=m/mi

令 t i t_i ti​为线性同余方程 t i M i ≡ 1 ( m o d m i ) t_iM_i≡1(mod \ m_i) ti​Mi​≡1(mod mi​)的一个解,也就是 M i M_i Mi​的逆元

则方程组的整数解为 ∑ i = 1 n a i M i t i ∑_{i=1}^na_iM_it_i ∑i=1n​ai​Mi​ti​

证明:

首先解释一下,一般的线性同余方程长这样:a*x≡b (mod m)

它的解有无数个,我们可以将通解表示为 x = x 0 + k t ( k ∈ Z ) x=x_0+kt \ (k∈Z) x=x0​+kt (k∈Z)

而这个通解其实就相当于 x ≡ x 0 ( m o d t ) x≡x_0(mod\ t) x≡x0​(mod t)

因此所有的线性同余方程组都可以转化为下面这种较为一般的形式

而孙子定理通过构造法得到了一种特殊情况下(即模数 m i m_i mi​两两互质时)的通解,换言之只要能保证模数两两互质则方程组一定有解

证明如下:

将解 ∑ i = 1 n a i M i t i ∑_{i=1}^na_iM_it_i ∑i=1n​ai​Mi​ti​代入原方程组任一方程

a 1 M 1 t 1 + a 2 M 2 t 2 + ⋅ ⋅ ⋅ + a i M i t i + ⋅ ⋅ ⋅ + a n M n t n ≡ a i ( m o d m i ) a_1M_1t_1+a_2M_2t_2+\cdot\cdot\cdot+a_iM_it_i+\cdot\cdot\cdot+a_nM_nt_n≡a_i(mod\ m_i) a1​M1​t1​+a2​M2​t2​+⋅⋅⋅+ai​Mi​ti​+⋅⋅⋅+an​Mn​tn​≡ai​(mod mi​)

上面这个式子显然是成立的

对于 k ≠ i k≠i k​=i的项, a k M k t k ≡ 0 ( m o d m i ) a_kM_kt_k≡0(mod\ m_i) ak​Mk​tk​≡0(mod mi​),因为 M k M_k Mk​是 m i m_i mi​的倍数

而 a i M i t i ≡ a i ( m o d m i ) a_iM_it_i≡a_i(mod\ m_i) ai​Mi​ti​≡ai​(mod mi​),因为 t i t_i ti​是 M i M_i Mi​在模 m i m_i mi​意义下的逆元, t i M i t_iM_i ti​Mi​在模 m i m_i mi​意义下等价于1

证毕

方程组的通解可以表示为 x = ∑ i = 1 n a i M i t i + k m x=∑_{i=1}^na_iM_it_i+km x=∑i=1n​ai​Mi​ti​+km

中国剩余定理的板子就不放了,毕竟都是些常规操作

需要注意的是中国剩余定理成立的前提是 m i m_i mi​互质,不要求 m i m_i mi​为素数,所以在求 t i t_i ti​时不要使用小费马定理,而应该用扩欧求逆元,在这里放一个扩欧求逆元的板子:

long long exgcd(long long a,long long b,long long &x,long long &y){if(a%b==0){x=0;y=1;return b;}long long d=exgcd(b,a%b,x,y);long long temp=x;x=y;y=temp-a/b*y;return d;
}long long inv(long long m,long long mod){ //求m在模mod下的逆元long long x,y;long long d=exgcd(m,mod,x,y);if(d!=1) return -1; //若不互质则无逆元long long temp=mod/d;return (x%temp+temp)%temp; //返回最小正整数解
}

一般的线性同余方程组:

中国剩余定理成立的前提是每个方程的模数两两互质,对于其他情况则不适用

可以使用n次扩展欧几里得算法进行求解:

假设已经求出了前k-1个方程构成的方程组的解 x x x, m m m为前k-1个方程模数 m i m_i mi​的最小公倍数

则前k-1个方程的通解可以表示为 x + i ∗ m , i ∈ Z x+i*m,i∈Z x+i∗m,i∈Z

为了使前k-1个方程的解也满足第k个方程,只能从通解中寻找

x + i ∗ m ≡ a k ( m o d m k ) x+i*m≡a_k(mod \ m_k) x+i∗m≡ak​(mod mk​)

只要寻找到满足条件的i即可

对该方程进行转化:

i ∗ m ≡ a k − x ( m o d m k ) i*m≡a_k-x \ (mod \ m_k) i∗m≡ak​−x (mod mk​)

此时只有 i i i是未知数,所以这就是一个线性同余方程,用一次扩展欧几里得就可得到它的一个解 i = t i=t i=t,则 x ′ = x + t ∗ m x^{'}=x+t*m x′=x+t∗m就是前k个方程的一个解

以此类推就能得到整个方程组的解

而如果在某次用扩展欧几里得运算时发现无解,则整个方程无解

6.原根

回顾一下欧拉定理:

若 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1,则 a φ ( m ) ≡ 1 ( m o d m ) a^{φ(m)}≡1\ (mod \ m) aφ(m)≡1 (mod m)

可以这么理解欧拉定理:

方程 a x ≡ d ( m o d m ) a^x ≡ d ( mod \ m) ax≡d(mod m)其实是在探究 a x % m a^x\%m ax%m的值

当x=0时, a 0 ≡ 1 ( m o d m ) a^0≡1 (mod \ m) a0≡1(mod m)是显然的

而随着a的指数不断增加,到 φ ( m ) φ(m) φ(m)时,必定会出现 a φ ( m ) ≡ 1 ( m o d m ) a^{φ(m)} ≡ 1 (mod \ m) aφ(m)≡1(mod m)

并且 a φ ( m ) + 1 ≡ a φ ( m ) ⋅ a 1 ≡ 1 ⋅ a ≡ a 1 ( m o d m ) a^{φ(m)+1}≡a^{φ(m)}\cdot a^1≡ 1 \cdot a≡a^1(mod \ m) aφ(m)+1≡aφ(m)⋅a1≡1⋅a≡a1(mod m)

也就是说a的幂次在模m的意义下是存在循环节的,并且这个循环节的长度不超过 φ ( m ) φ(m) φ(m)

注:前提是a、m互质

如果不太理解,可以学到后面原根的内容再回来看

通过欧拉定理,我们可以知道在1到m之间必然存在一个数x,使得 a x ≡ 1 ( m o d m ) a^x≡1(mod\ m) ax≡1(mod m),因为至少 φ ( m ) φ(m) φ(m)就是这样一个满足条件的x,但其实它不一定是最小的那个x

阶:

设 m > 1 , g c d ( a , m ) = 1 m>1,gcd(a,m)=1 m>1,gcd(a,m)=1,则使得 a r ≡ 1 ( m o d m ) a^r≡1(mod\ m) ar≡1(mod m)成立的最小正整数 r r r就是 a a a对于模 m m m的阶

这里的阶也就是上面说的循环节

阶的性质:

使得 a x ≡ 1 ( m o d m ) a^x≡1(mod\ m) ax≡1(mod m)成立的解 x x x一定满足 x % r = = 0 x\%r==0 x%r==0 或者记为 r ∣ x r|x r∣x(r为x的约数)

这是一个充要条件,即阶的倍数也一定满足该同余式,因为 a r k ≡ ( a r ) k ≡ 1 ( m o d m ) a^{rk}≡(a^r)^k≡1(mod \ m) ark≡(ar)k≡1(mod m)

提供一种求阶的办法:先算出 φ ( m ) φ(m) φ(m),然后从小到大遍历 φ ( m ) φ(m) φ(m)的约数,第一个满足 a x ≡ 1 ( m o d m ) a^x≡1(mod\ m) ax≡1(mod m)的数就是 a a a对于模 m m m的阶

原根:

对于某一些(a,m), a a a对于模 m m m的阶恰好为 φ ( m ) φ(m) φ(m),此时称 a a a为模 m m m的一个原根

原根的重要性质:

只有 2 、 4 、 p k 、 2 p k 2、4、p^k、2p^k 2、4、pk、2pk有原根,其中p为奇素数。该性质可以看出一个数有原根,则其最多有两个质因子

若一个数 m m m存在原根,则原根的个数为 φ ( φ ( m ) ) φ(φ(m)) φ(φ(m))

令 a a a为 m m m最小的原根

当 g c d ( k , φ ( m ) ) = 1 gcd(k,φ(m))=1 gcd(k,φ(m))=1时, a k a^k ak也是m的原根

证明不会

设 a a a为m的一个原根,则 a 0 , a 1 , a 2 ⋅ ⋅ ⋅ , a φ ( m ) − 1 a^0,a^1,a^2\cdot\cdot\cdot ,a^{φ(m)-1} a0,a1,a2⋅⋅⋅,aφ(m)−1的值各不相同,组成了 m m m的简化剩余系

很好理解,之前有讲过阶其实可以理解为循环节长度,一个循环节内的各个数是各不相同的,而当这个循环节长度为 φ ( m ) φ(m) φ(m)时,则a的幂次可以表现出 φ ( m ) φ(m) φ(m)个不同的数

常见(最小)原根:

2的原根是1;
4的原根是3;
998244353的原根是3;
1e9+7的原根是5

判断有无原根:

bool judge(int x){if(x==2||x==4) return true;if(x%2==0) x/=2;if(Isprime[x]) return true;  if(x%2==0) return false;  //2的幂次中只有2和4才有原根for(int i=0;i<num;i++){if(x%prime[i]==0){while(x%prime[i]==0)x/=prime[i];if(x!=1) return false;return true;}}return false;
}

求最小原根:

最小原根一般不大,所以暴力即可
从2开始遍历,然后检验是否为原根

检验的方法:

不妨用Y表示被检查的数,我们只要检查Y的阶是否为 φ ( m ) φ(m) φ(m)即可(原根的定义)

①运用反证法的思想,先假设Y不是原根,即阶不是 φ ( m ) φ(m) φ(m),则阶一定是 φ ( m ) φ(m) φ(m)的某个因子(阶的性质)。

②预处理出 φ ( m ) φ(m) φ(m)的所有质因子,比如质因子x,然后检验 a φ ( m ) / x % m a^{φ(m)/x}\%m aφ(m)/x%m是否为1

之所以检验 φ ( m ) / x φ(m)/x φ(m)/x是因为至少存在一个 φ ( m ) / x φ(m)/x φ(m)/x是阶的倍数,而阶的倍数一定也满足 a t ≡ 1 ( m o d m ) a^t≡1(mod\ m) at≡1(mod m)

③若所有的 φ ( m ) / x φ(m)/x φ(m)/x都不满足同余式,则假设出现矛盾,Y是原根。反之,则说明该数就不是原根。

模板:

phi数组为欧拉函数值

vector<long long> a;
bool check(long long  g,long long  p){ //检验g是否为p的原根for(long long i=0;i<a.size(); ++i)if(quickpow(g,phi[p]/a[i],p)==1)return 0;return 1;
}
long long  pri_root(long long  p){ //求p的最小原根a.clear();long long  temp=phi[p];for(long long i=2;i*i<=temp;i++) //phi[p]的所有质因子if(temp%i==0){a.push_back(i);while(temp%i==0)temp/=i;}if(temp>1)a.push_back(temp);for(long long g=1;g<p;g++){if(gcd(g,p)!=1) continue;if(check(g,p))return g;}
}

求所有原根的步骤:

1)求出最小原根

2)借助性质2求出所有原根(一个个求耗时太长)

模板:

void  solve(long long  p){ //求p的所有原根long long mi; //最小原根mi,求最小原根方法见上//下面借助最小原根求所有原根for(long long t=1;t<=phi[p];t++){if(gcd(t,phi[p])==1){ans.push_back(quickpow(mi,t,p));}}sort(ans.begin(),ans.end());
}

例题:

①POJ1284:求原根个数

②51Nod-1135:求奇素数的最小原根

③HDU4992:求一个数的所有原根

④URAL-1268:求最大原根(倒着枚举,若先求最小原根再去找最大原根会T)

应用:

①模p意义下的一些乘法可以转化为原根幂次的加法

②NTT中需要用到,但我还没学会,其他我暂时也没遇到,待填坑

7.高次同余方程

有两种高次同余方程:

a x ≡ b ( m o d m ) a^x≡b(mod \ m) ax≡b(mod m)以及 x a ≡ b ( m o d m ) x^a ≡ b(mod \ m) xa≡b(mod m)

————————————————————————

离散对数:

设 a a a为 m m m的一个原根,b、m互质

若 a x ≡ b ( m o d m ) a^x ≡ b\ (mod\ m) ax≡b (mod m),则记 x x x为 l o g a b ( m o d m ) log_ab\ (mod \ m) loga​b (mod m),称为指标

而离散对数则更一般化, a x ≡ b ( m o d m ) a^x≡b \ (mod \ m) ax≡b (mod m)的解 x x x就是离散对数

求离散对数,其实也就是求解第一种类型的高次同余方程了

BSGS算法:

Baby step giant step算法

设 x = k n − i x=kn-i x=kn−i,其中 n n n即为giant step(大步), i i i为baby step(小步),由自己事先确定

则方程变为 ( a n ) k ≡ b ∗ a i ( m o d m ) (a^n)^k≡b*a^i(mod\ m) (an)k≡b∗ai(mod m)

枚举 i ∈ [ 0 , n − 1 ] i∈[0,n-1] i∈[0,n−1],将 b ∗ a i b*a^i b∗ai的值插入Hash表

然后枚举 k k k,计算 ( a n ) k (a^n)^k (an)k,在Hash表中查找

复杂度: O ( n + m n ) O(n+\frac{m}{n}) O(n+nm​)

由于a、b、m互质,根据欧拉定理 x x x必定为 0 0 0到 φ ( m ) − 1 φ(m)-1 φ(m)−1中的某个值,则枚举大步需要 m n \frac{m}{n} nm​,枚举小步需要 n n n步

所以取 n n n为 √ m √m √m达到最优复杂度,即 O ( √ m ) O(√m) O(√m)

模板:

long long  bsgs(long long  a,long long b,long long  m){  //logab(mod m)map<long long ,long long >Hash;Hash.clear();b%=m;long long  n=(long long)sqrt(m)+1;  //大步for(int i=0;i<n;i++){   //枚举小步long long val=b*quickpow(a,i,m)%m;Hash[val]=i;}a=quickpow(a,n,m);if(a==0) return b==0?1:-1;for(long long k=0;k<=n;k++){ //枚举大步long long val=quickpow(a,k,m);long long i;if(i=Hash[val]){if(k*n-i>=0)return k*n-i;}}return -1;//没找到就是无解
}

需要注意的是该算法正确的前提是 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1

否则无法保证在 1 1 1到 φ ( m ) φ(m) φ(m)中可以找到解

扩展BSGS:

目的是在 a a a和 m m m不互质情况下求出离散对数值

基本思想将方程转化到 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1为止,a比较难变,但是模数m可以不断缩小至与a互质

令 d = g c d ( a , m ) d=gcd(a,m) d=gcd(a,m)

首先若d不是b的约数,则方程无解

证明:

将同余方程转化为不定方程

a ∗ a x − 1 + m ∗ t = b a*a^{x-1}+m*t=b a∗ax−1+m∗t=b

由贝祖定理即得,b必须是gcd(a,m)的倍数

然后开始转化方程

同余式同时除以d仍然成立(详见前面的同余基本性质), a d ⋅ a x − 1 ≡ b d ( m o d m d ) \frac{a}{d}\cdot a^{x-1}≡\frac{b}{d}(mod \ \frac{m}{d}) da​⋅ax−1≡db​(mod dm​)

若此时 a a a和 m d \frac{m}{d} dm​互质了,则我们可以使用BSGS算法去求解了,但若不互质,则我们再进行一轮转化

a 2 d 1 d 2 ⋅ a x − 2 ≡ b d 1 d 2 ( m o d m d 1 d 2 ) \frac{a^2}{d_1d_2}\cdot a^{x-2} ≡ \frac{b}{d_1d_2} (mod \frac{m}{d_1d_2}) d1​d2​a2​⋅ax−2≡d1​d2​b​(modd1​d2​m​)

注: d 1 d_1 d1​即之前的 g c d ( a , m ) gcd(a,m) gcd(a,m), d 2 d_2 d2​即 g c d ( a , m d 1 ) gcd(a,\frac{m}{d_1}) gcd(a,d1​m​)

如此重复直到

a k d 1 d 2 ⋅ ⋅ ⋅ d k ⋅ a x − k ≡ b d 1 d 2 ⋅ ⋅ ⋅ d k ( m o d m d 1 d 2 ⋅ ⋅ ⋅ d k ) \frac{a^k}{d_1d_2\cdot\cdot\cdot d_k}\cdot a^{x-k} ≡ \frac{b}{d_1d_2\cdot\cdot\cdot d_k} (mod \frac{m}{d_1d_2\cdot\cdot\cdot d_k}) d1​d2​⋅⋅⋅dk​ak​⋅ax−k≡d1​d2​⋅⋅⋅dk​b​(modd1​d2​⋅⋅⋅dk​m​)

将常数 a k d 1 d 2 ⋅ ⋅ ⋅ d k \frac{a^k}{d_1d_2\cdot\cdot\cdot d_k} d1​d2​⋅⋅⋅dk​ak​转移到同余式的右边,怎么转移呢?两边同时乘上该常数的逆元即可(用扩欧求)

解出来的值是 x − k x-k x−k,再加上 k k k即为答案

模板:

long long  exbsgs(long long  a,long long  p,long long  b){if(b==1||p==1) return 0;a%=p;b%=p;long long  d=gcd(a,p),k=0,t=1;while(d>1){if(b%d) return -1;k++;b/=d,p/=d,t*=(a/d),t%=p;if(t==b) return k;d=gcd(a,p);}long long  ans=bsgs(a,p,b*inv(t,p)%p);return ans==-1?-1:ans+k;
}

——————————————————————————

下面说一下另一种高次同余方程 x a ≡ b ( m o d m ) x^a ≡ b(mod \ m) xa≡b(mod m)的解法:

首先求出 m m m的原根 g g g,令 x = g u % m x=g^u\%m x=gu%m, b = g t % m b=g^t\%m b=gt%m(这么令,是因为原根的幂次可以表示m的简化剩余系),用BSGS算法求出t后,将方程写为 g a u ≡ g t ( m o d m ) g^{au}≡g^t (mod \ m) gau≡gt(mod m)

因为 g m − 1 % m = 1 g^{m-1}\%m=1 gm−1%m=1(费马小定理)

从而

g a u ⋅ ( g m − 1 ) v = g t g^{au}\cdot (g^{m-1})^v =g^t gau⋅(gm−1)v=gt

a u + ( m − 1 ) v = t au+(m-1)v=t au+(m−1)v=t

扩欧即可求出u,也就求出了x

BSGS、扩展欧几里得、求原根的方法前面都有了

【数论】ACM数论基础知识总结相关推荐

  1. 密码学基础知识-数论(从入门到放弃)

    数论知识 本文主要介绍整除.质数和合数.同余定理.模逆元素.欧几里得除法.欧拉函数.欧拉定理.费马小定理.中国剩余定理(孙子定理). 文章目录 数论知识 简介 一.整除 二.质数和合数 三.同余定理 ...

  2. 参加ACM比赛所需的基础知识

    一.语言是最重要的基本功 无论侧重于什么方面,只要是通过计算机程序去最终实现的竞赛,语言都是大家要过 的第一道关.亚洲赛区的比赛支持的语言包括C/C++与JAVA.笔者首先说说JAVA,众所 周知,作 ...

  3. 参加ACM比赛所需的基础知识(转)

    http://www.yuanma.org/data/2007/0612/article_2663.htm 一.语言是最重要的基本功                 无论侧重于什么方面,只要是通过计算 ...

  4. ACM数论专题3——素数(质数)

    ACM数论专题3--素数 素数是什么 蛮力算法求素数 蛮力算法的实现以及分析 时间复杂度 蛮力算法的改进 时间复杂度 **埃筛** 时间复杂度 线筛 线筛原理分析 线筛实现 素数是什么 质数1 (pr ...

  5. ACM StepByStep(一)基础知识与参考资料

    ACM-ICPC StepByStep(一)基础知识与参考资料 对于不是计算机专业的同学,如果一开始就从做题开始,可能并不是最好的方式.因为对编程环境和能够使用的工具了解不够,同时对于题目也不能区分哪 ...

  6. ACM竞赛需要的基础知识

    就快期末考试了,网课摸鱼了一个学期,赶紧复习一下上的ACM课程的知识.发现老师开头列的知识点好全面,先做个标记,分享一下给大家.

  7. 《openssl编程》之基础知识

    第一章 基础知识 1.1 对称算法 对称算法使用一个密钥.给定一个明文和一个密钥,加密产生密文,其长度和明文大致相同.解密时,使用读密钥与加密密钥相同. 对称算法主要有四种加密模式: (1) 电子密码 ...

  8. MATLAB学习笔记2:MATLAB基础知识(下)

    阅读前请注意: 1. 该学习笔记是华中师范大学HelloWorld程序设计协会2021年寒假MATLAB培训的学习记录,是基于培训课堂内容的总结归纳.拓展阅读.博客内容由 @K2SO4钾 撰写.编辑, ...

  9. 2021年5月信息系统项目管理师真题基础知识1~32题

    摘要:2021年5月信息系统项目管理师真题基础知识1~32题,视频解析可找科科过官网右侧联系方式获取. 1.[科科过]国家信息化体系包括六个要素,其中(1)信息化体系六要素中的龙头,是国家信息化建设的 ...

  10. 【学习笔记】网络安全基础知识总结

    网络安全基础知识总结 前言 一.网络安全概述 1.1 引言 1.2 密码学的发展 1.3 密码学基础 1.4 对称密码 1.4.1 数据加密标准DES 二.数论知识 2.1 数论基础 2.2 有限域 ...

最新文章

  1. 基于流式的md5计算-多线程下载工具Lwget介绍
  2. 【MATLAB】符号数学计算(二):符号运算中的运算符和函数
  3. 「CSDN 2021年度 IT 技术影响力之星评选」活动报名倒计时!
  4. NYOJ 士兵杀敌(四) 树状数组
  5. Docker镜像加速,设置国内源
  6. luogu P2512 [HAOI2008]糖果传递
  7. GlusterFS架构与维护
  8. 更了吗?Windows 11 22000.184 推送
  9. D - 又见回文---C11新标准
  10. Command line is too long. Shorten command line for Application---微服务升级_SpringCloud Alibaba工作笔记0067
  11. 【收藏】这些Python代码技巧,你肯定还不知道
  12. 国军标GJB150.8A-2009淋雨试验第三方检测机构
  13. MATLAB求解线性规划问题
  14. 四层协议和七层协议详解
  15. 电脑右键的新建怎么没有了
  16. 《戴上“白帽子”的黑客们:把漏洞变成礼物》
  17. 手机word文档docx密码忘了怎么办,忘记word文档docx密码怎么办?
  18. Python format 格式化输入字符对不齐解决
  19. qq浏览器打开word 技术原理_无需安装阅读器,PDF还可以这样打开,涨姿势了
  20. android b571 版本,HUAWEI Mate 7 EMUI 4.0.1 B571 版本发布说明及问题反馈

热门文章

  1. 中文单栏latex模板
  2. Jimu310 数据同步
  3. Python Unit Test - 3 pydoc
  4. 【图书管理系统】附源码+教程
  5. Spring监听器的完整使用步骤
  6. 是时候来了解android7了:shortcuts(快捷方式)
  7. Informatica PowerCenter工作流管理系统
  8. 【英语词组】恋恋不忘Day6-1
  9. DeepLabv2 caffe创建可编译环境
  10. 计算机word宏,Word用宏来列出所有可用样式-word技巧-电脑技巧收藏家