筛质数
c++
筛质数
/*
* 素数筛
* 素数、质数定义:
* 质数,又叫素数,是指一个大于1的自然数,且除了1和它本身外,不能被其他自然数整除的数。换句话说,就是该数除了1 和它本身以外,不再有其他的因数。
*
* 在先前的博客中,我们介绍了试除法 O(sqrt(N)) 判断一个数字是否为素数;以及 O(sqrt(N)) 将数分解为素数,下面我们介绍素数筛。
*
* 素数筛判断 1..n 中的数字,分别是否为素数。
* 1. 使用试除法作素数筛
* 我们已知试除法判断一个数字是否为素数的复杂度是 O(sqrt(N)),那么判断 n 个数字,复杂度约为 O(N sqrt(N)) = O(N ^ 1.5)。
* 这是因为试除法 判断第 i 个数字是否为素数,没有充分利用到前 i 个数字的信息
*
* 因此可以充分使用了前面素数信息的方法,思想是判断 x 是否为素数,查看 x 之前,并且小于 sqrt(x) 的素数,x是否可以整除。
*
* π(x)符号,就是表示小于或等于自然数x的所有素数个数,当x趋于无穷大时,π(x)和x /ln(x)这两个数的比值趋于1。
* 因此复杂度为 O(N log(sqrt(N))
* 2. 埃氏筛
* 使用改进后的试除法作为素数筛是可以,但是 x 老是会整一些除不尽的数,浪费时间,因此我们可以使用质数,否定他的倍数。
* 计算量:
* n / 1 + n / 2 + n / 3 + ... + n / n = n logn
* 但是加上素数优化,感觉会好很多。
*
* 3. 欧拉筛(线性筛)
* 欧拉筛和埃氏筛都是一样,利用 st 数组,结合公倍数进行筛选,晒出合数,剩下的就是质数了,他们复杂度的优劣性在于合数被筛选了多少次?
* 埃氏筛的算法流程可以看出,对于某个合数,他被筛出的次数为他质因数分解后,质数的个数,如 45 = 3 ^ 2 * 5,他有两个种类的质数,3和5。
* 欧拉筛的出发点在于,能否每个合数只需要筛选一次?
* -> 我们使用 最小的质因数 筛选合数,那么这个合数只会被筛选一次。
* 复杂度从名称也可以听出来,为 O(N)
*
* 4. 实际运行时间分析
* 发现试除法还是耗时长,这是因为试除法所有数字都要试除,不如直接利用 st 方法筛选出他们的倍数方便。
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdio>
using namespace std;
const int N = 1000010;
int n;
vector<int> prime;
bool st[N];
// 优化之后的试除法判质数
// @test-time: 231 ms
void sample_seive(int n) {
prime.clear();
memset(st, false, sizeof st);
if (n <= 1) {
return;
}
st[1] = true;
for (int i = 2; i <= n; i ++ ) {
for (int j = 0; j < prime.size() && prime[j] * prime[j] <= i; j ++ ) {
if (i % prime[j] == 0) {
st[i] = true;
break;
}
}
if (st[i] == false) {
prime.push_back(i);
}
}
}
// 埃氏筛
// @test-time: 53 ms
void ai_sieve(int n) {
memset(st, false, sizeof st);
prime.clear();
if (n <= 1) {
return;
}
st[1] = true;
for (int i = 2; i <= n; i ++ ) {
if (st[i] == false) {
prime.push_back(i);
for (int j = i + i; j <= n; j += i) {
st[j] = true;
}
}
}
}
// 欧拉筛,线性筛
// @test-time:
void euler_sieve(int n) {
prime.clear();
memset(st, false, sizeof st);
if (n <= 1) {
return;
}
for (int i = 2; i <= n; i ++ ) {
if (st[i] == false) {
prime.push_back(i);
}
for (int j = 0; prime[j] <= n / i; j ++ ) { // i 这个因数,分解后质因数小于等于 primes[j]
st[i * prime[j]] = true;
if (i % prime[j] == 0) {
break;
}
}
}
}
int main()
{
scanf("%d", &n);
euler_sieve(n);
printf("%d\n", prime.size());
return 0;
}