设 a,b 时两个整数,且 b ≠ 0,如果存在整数c,使得 a = b * c,则称 a 被 b 整 除 a 被 b\color{Orange}整除 a被b整除 , b 整 除 b \color{Orange}整除 b整除a,即 a 是 b 的倍数,b 是 a 的因子。
素数(质数):
如果一个整数 a 大于 1 且只能被 1 和它自己整除,则称a 为素数
合数
如果a 大于 1 且不是素数,则称a 为合数
思路:如果 x 除了它本身还能被其他数整除,那么它不是素数,反之则是素数
代码模板
bool isPrime(int x)
{
if(x < 2) return false;
for(int i = 2; i < x; i ++ )
if(x % i == 0)
return false;
return true;
}
证明
若 a 是 一个合数,则存在素数 p , 使得 p | a
若 d ∣ x d | x d∣x ,显然 x d ∣ x \frac{x}d | x dx∣x,即 x 的质因子成对存在,故只需枚举每对中较小的那一个就行,即 x < = x d x <= \frac{x}d x<=dx → x < = √ n x <= √n x<=√n
代码模板
bool is_prime(int x)
{
if(x < 2) return false;
for(int i = 2; i <= x / i; i ++ )// 不写成 i <= sqrt(x)是为了防止爆 int
if(x % i == 0)
return false;
}
思路
把一个合数分解成若干个质因数的乘积的形式,即求质因数的过程叫做分解质因数
算 术 基 本 定 理 \color{Green}算术基本定理 算术基本定理
设 a > 1 ,则
a = p 1 α 1 ∗ p 2 α 2 ∗ ⋅ ⋅ ⋅ p k α k a = p_1^{α_1}*p_2^{α_2}*···p_k^{α_k} a=p1α1∗p2α2∗⋅⋅⋅pkαk
其中 p 1 , p 2 , ⋅ ⋅ ⋅ ⋅ , p k p_1,\ p_2,\ ····,\ p_k p1, p2, ⋅⋅⋅⋅, pk是不同的素数
α 1 , α 2 , ⋅ ⋅ ⋅ ⋅ , α k α_1,\ α_2,\ ····,\ α_k α1, α2, ⋅⋅⋅⋅, αk是正整数
并且在不记顺序的情况下,该表示是唯一的
如
30 = 2 × 3 × 5 30 = 2 × 3 × 5 30=2×3×5
88 = 2 3 × 11 88 = 2^3 × 11 88=23×11
代码模板
void divide(int x)
{
for(int i = 2 ; i <= x/i; i ++ )
if(x % i == 0)
{
int s = 0 ; // 各种 指数 α
while(x % i == 0) x /= i , s ++;
cout<< i <<" " << s << "\n";
}
if(x > 1) cout<< x << " " << 1 <<"\n"; // 只剩余 它本身 和 1
}
埃拉托色尼选筛法,是古希腊数学家埃拉托色尼提出的一种筛选法。
该筛法基于这样的想法:任意大于1的正整数 x 的倍数 2 x , 3 x , . . . 2x,3x,... 2x,3x,...都不是质数。根据质数的定义,上述命题显然成立。
从 2 开始,由小到达扫描每个 x ,把它的倍数 x , ⋯ , ⌊ N / x ⌋ ∗ x x,⋯,⌊N/x⌋∗x x,⋯,⌊N/x⌋∗x标记为合数。
每当扫描到一个数时,若它尚未被标记,则它不能被 2 ∼ x − 1 2∼x−1 2∼x−1之间的任何数整除,该数就是质数。
Eratosthenes 筛法过程如下:
该算法复杂度为 ∑ 质 数 p ≤ x x p = O ( n l o g l o g n ) \sum_{质数p ≤ x }\frac{x}p = O(nloglogn) ∑质数p≤xpx=O(nloglogn)。效率已经非常接近线性,是最常用的质数筛法
代码模板
int primes[N], cnt; // primes[ ] 存储所有素数,cnt 记录 筛到哪了
bool st[N]; //st[x] 存储 x 是否被 筛掉
void get_primes(int x)
{
for(int i = 2; i <= x; i ++ )
{
if(st[i]) continue; // 被筛过了,下一个
primes[i] = true;
for(int j = i + i; j <= n; j += i)
st[j] = true;
}
}
埃氏筛仍然做了许多重复工作。多个素数可能重复筛去同一个合数,于是有了欧拉筛。
原理
理解
当 p r i m e s [ j ] \ primes[\ j\ ] primes[ j ]可以 整除 i \ i i 时,即 i f ( i % p j = = 0 ) if( i \ \% \ pj == 0) if(i % pj==0),就应该退出,这样就保证每一个数都只被最小的质因子筛了一次
当 p r i m e s [ j ] primes[\ j\ ] primes[ j ]不可以 整除 i \ i i 时,即 i f ( i % p j ! = 0 ) if( i \ \% \ pj \ != 0) if(i % pj !=0),则
对于 i \ i i 的最小质因子 p > p r i m e s [ i ] p >primes[\ i\ ] p>primes[ i ](因素数表从小到大遍历),且因为 p > p r i m e s [ j ] p >primes[\ j\ ] p>primes[ j ],即 pj 一定小于 i \ i i 的所有质因子,pj 也一定是 x = p r i m e s [ j ] ∗ i x =primes[\ j \ ] * i x=primes[ j ]∗i 的最小质因子
此外是否还会存在被重复筛的情况,如果 x 还存在比 p r i m e s [ j ] primes[\ j\ ] primes[ j ] 大的质因子,
则 x 可以写作
但是我们的 i \ i i是从小到大枚举的, x \ x x 被筛过之后不可能出现 i − v a r \ i\ −\ var i − var的情况。
还需要使得每个合数都被筛掉,对于一个合数 x 被筛时一定是 x = p r i m e s [ j ] ∗ i \ x\ =\ primes[\ j\ ]\ ∗ \ i x = primes[ j ] ∗ i,遍历 x x x之前一定遍历过了 i i i,则$x 一 定 会 通 过 i 一定会通过 i 一定会通过i被筛掉。
为什么线性
对于一个合数 x,假设 pj 是 x 的最小质因子,在 i 枚举到 x 之前一定会枚举到 x / pj,故在循环语句中 x 中的合数一定会被筛掉,又每个数只会被筛一次,所以整个过程是线性的
代码模板
int primes[N], cnt; // primes[] 存储所有素数,cnt 记录 筛到哪了
bool st[N];//st[x] 存储 x 是否被 筛掉
void get_primes(int x)
{
for(int i = 2 ; i <= x; i ++ )
{
if(!st[i]) primes[cnt++] = i; // 最小质因子
for(int j = 0; primes[j] <= x/i; j ++ )
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}