引入
题目背景
给定正整数 n n n,请求出 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n间所有质数
输入格式
一个正整数 n n n。
输出格式
一行多个正整数,表示 1 , 2 , ⋯ , n 1,2,\cdots,n 1,2,⋯,n中所有质数。
样例
样例 #1
样例输入 #1
3
样例输出 #1
2 3
数据范围
- 对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 1 0 6 1\leq n\leq10^6 1≤n≤106
暴力解决
遇事不决先暴力——洛谷大犇
最基础的思想就是枚举一遍
1
,
2
,
⋯
,
n
1,2,\cdots,n
1,2,⋯,n,遇到质数就输出。
实现代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool isprime(int x)
{
if (x == 1) return 0;
int i = 2;
while (i < x) {
if (x % i == 0) return 0;
++i;
}
return 1;
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; ++i)
if (isprime(i)) printf("%d ", i);
printf("\n");
return 0;
}
时间复杂度: O ( n 2 ) O(n^2) O(n2)
我们注意到,任何数
i
(
x
≤
i
<
x
)
i(\sqrt x\leq i<x)
i(x≤i<x) 一定不整除
x
x
x。
所以枚举
i
i
i 时只需枚举
2
,
⋯
,
x
2,\cdots,\sqrt x
2,⋯,x 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool isprime(int x)
{
if (x == 1) return 0;
int i = 2;
while (i * i <= x) { // any number that is bigger than sqrt(x) and smaller than x won't be a factor of x
if (x % i == 0) return 0;
++i;
}
return 1;
}
int main()
{
scanf("%d", &n);
for (int i = 2; i <= n; ++i)
if (isprime(i)) printf("%d ", i);
printf("\n");
return 0;
}
于是时间复杂度降到 O ( n n ) O(n\sqrt n) O(nn)。
埃氏筛基本思想
上面的代码中,每一个数都要判断是否被
2
2
2 整除。但是我们明显可以看到,只有
2
2
2是质数,其他偶数必定不是质数。所以这样实质上浪费了
O
(
n
)
O(n)
O(n)的时间。
同理,对于一个质数
p
p
p,
k
p
(
k
≥
2
且
k
∈
Z
+
)
kp(k\geq2且k\in\Z^+)
kp(k≥2且k∈Z+)1 必有
p
p
p 这个因数,所以
k
p
kp
kp 一定不是质数。
那么,我们可以想到,不枚举
1
,
⋯
,
n
1,\cdots,n
1,⋯,n,转而枚举
p
1
,
p
2
,
⋯
,
p
m
p_1,p_2,\cdots,p_m
p1,p2,⋯,pm,并将
{
x
∣
x
=
k
p
i
}
(
k
≥
2
)
\{x\mid x=kp_i\}(k\geq2)
{x∣x=kpi}(k≥2) 这个集合里所有数打上非质数
标记(这里用布尔值最为方便,1
表示不是质数)。
埃氏筛模版
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
int n;
bool p[N] = {1, 1}; // p[i]: if i is not prime then p[i] = 1, otherwise it'll be 0
void fillp()
{
for (int i = 2; i * i <= n; ++i)
if (!p[i])
for (int j = 2; i * j <= n; ++j) p[i * j] = 1;
}
int main()
{
scanf("%d", &n);
fillp();
for (int i = 2; i <= n; ++i)
if (!p[i]) printf("%d ", i);
return 0;
}
这样,我们省下很多重复的计算。
时间复杂度:
O
(
n
log
log
n
)
O(n\log\log n)
O(nloglogn)2
这并不是没有代价的,空间复杂度从
O
(
1
)
O(1)
O(1) 升到了
O
(
n
)
O(n)
O(n),类似dp
以空间换时间的做法。
Z + \Z^+ Z+为正整数集,即 { 1 , 2 , ⋯ } \{1,2,\cdots\} {1,2,⋯} ↩︎
作者觉得还可以的证明:有点不严谨,但不繁琐 ↩︎