质数
质数基本性质
概念
- 对于一正整数 a,如果 a 的因数有且仅有 1 和 a,则称 a 为质数(素数),否则称 a 为合数。
质数定理
- 在整个自然数集合中,质数分布相对稀疏,对于一个足够大的数 N,[1,N] 中所有的质数个数为:
质数的判定
试除法
-
模版题AcWing 866. 试除法判定质数 枚举 [2,x] 所有整数,如果
x%i == 0则 x 不是质数。 -
优化 容易发现,合数 x 的因数总是成对出现,并且关于 对称,因此只将枚举区间缩小至 [2,] 即可,每次判断 x 是否为质数时间复杂度为严格 O()。
-
代码
bool is_prime(int x)
{
if (x <= 1)
return false;
/*
这里建议写 i<=x/i
原因:
1)写 sqrt(x),要调用 <cmath> 的函数,太慢、
2)写 i*i<=x, 如果 i 过大会溢出。
*/
for (int i = 2; i <= x / i; i++) {if (x % i == 0)
return false;
}
return true;
}
质数的筛选
埃氏筛法
- 对于任意 x,其倍数 2x,3x... 必然不是质数, 从 2 开始扫描所有小于 N 的数,如果当前数为质数,筛除其小于等于 N 的所有倍数。直到筛去 [1,N] 中的所有质数的倍数,就可以筛去区间内所有合数。
- 优化:
由于对于数字 6,会被 2,3 各筛一遍,因此若当前数 x 为质数,小于
x^2的所有合数已经之前的质数筛去了,只需要筛除 [, [] ] 的所有 x 的倍数即可。 - 筛除图解——(来自 李煜东 《算法竞赛进阶指南》)
- 复杂度分析 每次将 x 的倍数筛除,需要计算:
此式为 调和级数:
因此复杂度为:O()。
- 代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
void Eratosthenes(int n)
{for (int i = 2; i <= n; i++) {if (st[i])
continue;
primes[++cnt] = i; // 如果没被筛过,就是质数
for (int j = i; j <= n / i; j++) // 从 x^2 开始筛去 x 的倍数
st[j * i] = true;
}
}
线性筛法
- 上述从 开始还是会重复筛除,eg:12 ,被 2,3,6 重复标记,因此需要对产生 12 的方式进行固定,只让其被筛除一次以达到线性复杂度 (O(n))。
- 线性筛法,是将一个数 x,只使 x 被其的最小质因数筛除,eg:12 仅被 2 筛除。
- 实现方式:
在埃式筛法的基础上,每次扫描不大于 的所有质数,将
st[primes[j] * i ] = true筛除。 - 筛除图解——(来自 李煜东 《算法竞赛进阶指南》)!
- 代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
void get_primes(int n)
{for (int i = 2; i <= n; i++) {if (!st[i])
primes[++cnt] = i; // 如果没被筛过,就是质数
for (int j = 0; primes[j] <= n / i; j++){if (i % primes[j] == 0) // 如果可以整除,则存在比当前更小的 prime 可以筛去 i,退出
break;
st[primes[j] * i] = true; //primes[j] 是 primes[j] * i 的最小质因子
}
}
}