数学及数学相关 学习笔记

发布时间 2023-11-27 22:43:40作者: ChthollyNS

数学及数学相关

目录

  1. 前置知识与符号定义

  2. 快速幂

  3. 素数筛

  4. 裴蜀定理

  5. 扩展欧几里得算法(exgcd)

  6. 同余方程

  7. 费马小定理

  8. 模意义下的乘法逆元

  9. 欧拉定理

  10. 卢卡斯定理

  11. 中国剩余定理

0.前置知识与符号定义

0.0 缺省源

由于篇幅原因,下文的代码自动省略以下片段:

#include <bits/stdc++.h>
#define int long long 
using namespace std;

0.1. 素数

对于一个数 \(p\)\(p \ne 0,\pm 1\),且 \(p\) 没有除 \(1\)\(p\) 以外的因数,则认为 \(p\) 是素数,否则认为 \(p\) 是合数。

0.2. 最大公因数

定义 \(\gcd(a,b)\)\(a,b\) 两数的最大公因数,最大公因数是指同时是 \(a,b\) 的因数的数。

0.3. 整除符号

定义 \(d|a\) 表示 \(a\) 可被 \(d\) 整除。

0.4. 模运算

定义 \(a \mod b=a-\lfloor \frac{a}{b} \rfloor \times b\)

特殊的,当输出一个数 \(k\)\(p\) 取模的结果时,请使用 cout<<(k%p+p)%p 而非 cout<<k%p,这可能导致一些符号错误。

1. 快速幂

模板题

对于快速幂的问题定义如下:给定两个整数 \(a,b\),求 \(a^b \mod k\)

对于此类问题,暴力的时间复杂度是显然 \(O(n)\) 的,考虑优化。

我们不妨将指数采用二进制表述,设指数的二进制串为 \(S_{i}\)\(tmp\) 表示当前二进制位对答案的贡献,\(a\) 表示底数。

\(S_{k}=1\),则 \(ans \gets ans \times tmp\)。由于第 \(k\) 位对答案的贡献为\(a^{2^{k-1}} \times a^{2^{k-1}}\),即 \(a^{tmp} \times a^{tmp}\),化简得 \(a^{tmp^2}\) 所以我们在每次操作后使 \(tmp \gets tmp \times tmp\) 即可。

其时间复杂度为 \(O(\log n)\)

代码:

int m;
int qpow(int a,int b)
{
	int ans=1,tmp=a;
	while(b!=0)
	{
		if(b&1)
		{
			ans=(ans%m*tmp%m)%m;
		}
		tmp=tmp*tmp%m;
		b>>=1;
	}
	return ans;
}

2. 素数筛

2.1. 埃氏筛

对于素数筛问题的模板题

埃氏筛的思想如下,若我们需要筛出小于等于 \(n\) 的素数,可以枚举 \(2\)\(n\) ,若当前枚举的数 \(k\) 是一个素数,则我们将 \(k\) 的倍数都标志为合数。特殊的,我们不去关心小于 \(k\)\(k\) 倍的数,因为该数在被 \(k\) 考虑之前一定被小于 \(k\) 的数考虑过。

可以证明,其时间复杂度为 \(O(n \log \log n)\)

以下代码实现筛出 \(n\) 以内的质数:

void prime(int n)
{
	int t=0;
	for(int i=2;i<=n;i++)
		isprime[i]=1;
	for(int i=2;i<=n;i++)
	{
		if(isprime[i])
		{
			prime[++t]=i;
			if(i*i<=n)
				for(int j=i*i;j<=n;j+=i)
					isprime[j]=0;
		}
	}
}

2.2.欧拉筛

欧拉筛模板题

埃氏筛的时间复杂度为 \(O(n \log \log n)\),无法通过本题。

欧拉筛的时间复杂度为 \(O(n)\)

对于欧拉筛,每一个数会且仅会被筛到一次。

其做法是对于一个数 \(k\),遍历当前质数表 \(S\)\(S\) 呈单调递增),若 \(k \mod S_{i}=0\) 则停止遍历。

容易证明其正确性,若 \(k\mod S_{i}=0\),则说明 \(S_{i}\)\(k\) 的最小质因数。即 \(S_{i+1}\) 一定不是其最小质因数。那么 \(i\) 乘上 \(S_{k+a}\) 的结果一定会被 \(S_{k}\) 的倍数筛到。

附代码:

void prime(int n)
{
	int t=0;
	for(int i=2;i<=n;i++)
	{
		if(!chck[i])
			pri[++t]=i;
		for(int j=1;i*pri[j]<=n&&j<=t;j++)
		{
			chck[i*pri[j]]=1;
			if(i%pri[j]==0)
				break;
			
		}
	}
}

3. 裴蜀定理

裴蜀定理的描述如下:

对于整数 \(a,b\)\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=\gcd(a,b)\) 一定有解。

有以下推论:

  • 对于整数 \(a,b\)\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=d\) ,则 \(d=\gcd(a,b)\)(逆定理)。

  • 对于一个数列 \(a\)\(a\) 中元素不全为 \(0\)), \(\sum^{n}_{i=1} a_{i}x_{i}=\gcd^n_{i=1}a_{i}\) 一定有解。

该定理证明见 4.3。

4. 扩展欧几里得算法(exgcd)

4.1. 辗转相除法(欧几里得算法)

辗转相除法可以用来求解最大公因数等问题。

其主要算法流程如下:

  1. 给定两数 \(a,b\),我们认为 \(a>b\)

  2. \(b=0\) 返回 \(a\)。否则递归求解 \(\gcd(b,a \mod b)\)

算法正确性证明如下:

首先证明 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。

我们设 \(a=bk+c\),则 \(\gcd(a,a \mod b)=c\),设 \(d|a\)\(d|b\) ,则有 \(c=a-bk\)\(\dfrac{c}{d}=\dfrac{a}{d}-\dfrac{b}{d}k\)

不难发现 \(\dfrac{c}{d}\) 是整数,即 \(d|c\),所以 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。

逆命题容易证明。

Q.E.D

代码如下:

int gcd(int a,int b)
{
	if(b==0) return a;
	else return gcd(b,a%b);
}

对于两整数 \(a,b\),有:

\[\gcd(a,b) \times \operatorname{lcm}(a,b) = a b \]

在此不做证明。

由此,有:

int lcm(int a,int b)
{
	return a*b/gcd(a,b);
}

请注意尽量不要使用 <cmath><algorithm> 库中的 __gcd() 函数,这可能导致被卡常。

请注意 <numeric> 库中的 std::gcdstd::lcm 仅在 C++ 17 及以上标准中可用,也就是说你在 CCF 的机子上用了就会喜提 CE 0 pts

4.2 扩展欧几里得算法

4.2.1. exgcd与裴蜀定理

扩展欧几里得算法(exgcd)常用于这样一类问题:给定一个不定方程 \(ax+by=c\),求其一组合法的整数解,即裴蜀定理。

即:求 \(ax+by=\gcd(a,b)\),并将求得的 \(x,y\) 分别乘以 \(\dfrac{c}{\gcd(a,b)}\)

我们可以尝试将问题转化为 4.1 中的欧几里得算法问题,即为 exgcd 算法。

与欧几里得法相似的,我们可以将 \(ax+by=\gcd(a,b)\) 递归为 \(bx'+(a \mod b)y' = \gcd(b,a \mod b)\)

特殊的,exgcd 中 \(ax+by=\gcd(a,b)\)\(bx'+(a \mod b)y' = \gcd(b,a \mod b)\) 并不等价。

由欧几里得算法:

\[bx'+(a \mod b)y' = \gcd(a,b)\iff bx'+(a \mod b)y' = \gcd(b,a \mod b) \]

假设 \(bx'+(a \mod b)y ' = \gcd(a,b)\) 中,\(x',y'\) 已知,由模运算定义:

\[ay'+b(x'-\lfloor\frac{a}{b}\rfloor y')=\gcd(a,b) \]

所以

\[x=y',y=x'-\lfloor\frac{a}{b}\rfloor y' \]

由定义,\(\gcd(a,0)=a\),此时 \(x=1,y=0\)

所以我们只需要递归到 \(b=0\) 时终止即可。

于是我们求出了不定方程 \(ax+by=c\) 的一组整数解,这也间接地证明了裴蜀定理。

附代码:

int exgcd(int a,int b,int &x,int &y)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	int h=exgcd(b,a%b,x,y);
	int xx=x,yy=y;
	x=yy,y=xx-a/b*yy;
	return h;
}

4.2.2. exgcd 求不定方程通解

假设我们已经知道了不定方程 \(ax+by=c\) 的一组解 \(x_{0},y_{0}\),若 \(x_{0}\) 增加 \(r\) 则方程左边增加 \(ar\),为保证等式成立,\(by\) 需减去 \(ar\),同时,\(y\) 是整数。这意味着 \(ar\)\(b\) 的倍数。

我们不妨设 \(t=ar\),因为 \(ar\)\(b\) 的倍数,所以最小的 \(t=\operatorname{lcm}(a,b)\)

由最小公倍数性质:

\[\operatorname{lcm}(a,b)=ar=\dfrac{ab}{\gcd(a,b)} \]

带回原式,得:

\[r=\dfrac{b}{\gcd(a,b)} \]

所以,\(y\)需减去:

\[\dfrac{ar}{b}=\dfrac{\frac{ab}{\gcd(a,b)}}{b}=\dfrac{a}{\gcd(a,b)} \]

所以,不定方程 \(ax+by=c\) 的通解是:

\[x=x_{0}+\dfrac{b}{\gcd(a,b)}k,y=y_{0}-\dfrac{a}{\gcd(a,b)}k \]

4.2.3 不定方程的无解情况

这与裴蜀定理所讨论的情况不同

对于不定方程 \(ax+by=c\) 其有解的必要条件是 \(c \bmod \gcd(a,b) = 0\)

证明如下:由最大公因数定义,\(a,b\) 都是 \(\gcd(a,b)\) 的倍数,所以 \(ax+by\)\(\gcd(a,b)\) 的倍数,即 \(c\)\(\gcd(a,b)\) 的倍数。若 \(c \bmod \gcd(a,b) \ne 0\),与 \(c\)\(\gcd(a,b)\) 的倍数矛盾,不成立。此时不定方程无解。

5. 同余方程

同余方程模板题

同余方程指的是形如 \(ax \equiv 1 \pmod b\) 的方程。

对于这种方程,我们可以将其转化为上文的不定方程 \(ax+by=c\)

为什么?

\(a \equiv b \pmod p\) 的本质是 \(a\)\(b\) 之间相差了若干个 \(p\)

所以\(ax \equiv 1 \pmod b \iff ax+by \equiv 1\)

特殊的,与不定方程类似,同余方程同样可能无解,这发生在 \(a,p\) 不互质的情况,证明如下:

对于不定方程 \(ax+by=1\),其有解的条件是 \(1 \bmod \gcd(a,b)=0\),即 \(\gcd(a,b)\) 必须为 \(1\),即两数互质。

5.1. 同余方程的性质

咕咕咕

6. 费马小定理

费马小定理的描述如下:若 \(p\) 为素数,\(\gcd(a,p)=1\),则 \(a^{p-1}\equiv 1 \pmod p\)

费马小定理是欧拉定理的一个推论,将在下文证明其正确性。

7. 模意义下的乘法逆元

所以我们有必要先知道什么是模意义下的乘法逆元(本文不讨论其它逆元,下文逆元皆指“模意义下的乘法逆元”)。

给定二个整数 \(a, p\),若 \(ax \equiv 1\pmod p\),则称 \(x\)\(a\) 的逆元。

7.1. exgcd求逆元

逆元模板题

显然可以用 5. 中提到的同余方程求解。

需要注意的是,由于不定方程可能无解,所以该种求逆元方式只适合 \(a,p\) 互质时。

值得注意的是,这里要求 \(a,p\) 互质,而费马小定理还要求 \(p\) 一定为质数

7.2. 费马小定理求逆元

由费马小定理,\(a^{p-1}\equiv 1 \pmod p\)

而逆元要求的式子是 \(ax \equiv 1 \pmod p\)

不难发现 \(x=a^{p-2}\),快速幂求出即可。

代码如下:

int pow(int a,int p)
{
	int poww=p-2;
	int pos=a,ans=1;
	while(poww)
	{
		if(poww&1)
			ans=ans*pos%p;
		pos=pos*pos%p;
		poww>>=1;
	}
	return ans;
}

时间复杂度 \(O(n\log n)\)

7.3. 线性求逆元

todo

8.欧拉定理

我们设 \(\phi(i)\)\(1<=k<=i\) 中与 \(i\) 互质的数的个数。

欧拉定理如下:

\[a^{\phi(i)}\equiv 1 \pmod i \]

可以用一种构造的方式证明其正确性:


wrote on \(Nov.3^{rd},2023\)