目录
本文目录
- 素数与筛法
- GCD与LCM
- 快速幂
素数与筛法
素数的定义
素数定义
素数(
形式上,可以表示为:
- 一个自然数
,若 且对于所有的 ,如果 能整除 (即 ),那么 或 。
素数的筛法
1.试除筛
对于一个大于1的数
1 | bool isPrime(int n) { |
2.埃氏筛
标记素数在范围内的所有倍数为非素数,因为对于一个合数的倍数,肯定也是一个比它小的素数的倍数。因为合数肯定有素数因子,即合数至少是一个素数的倍数。
所以合数的倍数也是素数的倍数,在枚举过程中,我们遇到一个合数选择跳过,遇到素数,那么标记该素数的所有倍数为非素数。 复杂度
1 | bool is_prime[MAXN]; |
3.欧拉筛
我们发现埃氏筛中,有的数会被重复枚举到。即一个数可以是多个素数的倍数。
比如210,它的质因子同时包含了2、3、5、7,所以会在枚举该四个素数的过程中重复枚举到。
欧拉线性筛的关键就在于,使得每个合数只被它的最小质因子筛除,从而保证每个数只会被标记一次。
核心思想:
- 对于每个数
,枚举所有小于等于 的最小质因数 ,标记 为合数。 - 当
能被 整除时,说明 是 的最小质因子,此时停止标记。
1 | vector<int> primes; // 存储质数 |
实际上内层循环的次数为:
用数学公式表示为:
而每个数字只会被其最小质因子标记,实际标记次数远小于
GCD与LCM
部分公式符号说明
- 整除符号
如果整数
- 取余符号
如果
- gcd(求最大公因数)
规定
- lcm(求最小公倍数)
辗转相除法求gcd(最大公因数)
辗转相除法的证明
设
令
由
则
因此
且
设
由
因此,
综上可得:
则
则
不断辗转相除
直到
gcd代码
1 | int gcd(int a, int b) |
lcm(最小公倍数)
根据算数基本定理,任何大于1的整数都可以唯一的分解成为一组质数的乘积,而且这种分解是唯一的。
分别对
则有
所以可以得到
互质
如果两个数只有1这一个公因数,那么这两个数互质,即
快速幂
快速幂的原理
如果我们要求解
,向下递归求解 ,然后将得到的结果平方,即 ,向下递归求解 ,然后将结果平方后乘以b,即
每次求解
快速幂的实现
1 | long long qpow(int y,int x) |
当然调用递归函数会导致常数较大,你可以选择非递归的写法,下面提供一下:
1 | long long qpow(long long y,long long x) |
当然你也可以用一行代码来实现
1 | int qpow(int a, int b) {int res = 1; for(; b; b >>= 1, a = a * a % MOD) if (b & 1) res = res * a % MOD; return res;} |