问题引出
问题:请将1~10000中的所有素数打印出来。
解决此类问题有一个通用的办法——用一个神奇的筛子,将所有素数全部“筛”出来。
基本数学背景
任何合数都可以唯一分解成一组素数的积。例如:60 = 2 * 2 * 3 * 5
筛子种类
素数筛一般有两种:埃氏筛和欧拉筛。前者时间复杂度为O(nloglogn)(我也不知道为什么),后者时间复杂度为O(n)。
埃式筛
- 核心思想
下列一串数中
2 3 4 5 6 7 8 9 10 11 12
2是素数,因此将2选出来放在一个专门装素数的数组中,并且将所有2的倍数的数全部“筛”掉。
2 3
456789101112(筛掉了4, 6, 8, 10, 12)
接下来,3并没有被“筛”掉,说明3是素数,将3选出来放在一个专门装素数的数组中,并且将所有3的倍数的数全部“筛”掉。
2 3
456789101112(筛掉了9)
接下来,发现4已经被筛掉,直接进行下一步。
5没有被筛掉,说明5是素数,将5选出来放在一个专门装素数的数组中,并且将所有5的倍数的数全部“筛”掉。
.一直到最后一个数终止。这就是埃氏筛的思路。
- 缺点
会出现重复筛选的过程,例如,10会被2筛掉,也会被5筛掉。12会被2筛掉,也会被3筛掉。
- 代码如下
#include<iostream>
using namespace std;
const int N = 100010;
int pr[N], cnt;
bool st[N];
void GetPrimes(int n) {
for (int i = 2; i < n; i++)
if (!st[i]) {
pr[cnt++] = i;
for (int j = 1; i * j < n; j++)
st[i * j] = true;
}
}
int main() {
int n;
cin >> n;
GetPrimes(n);
for (int i = 0; i < cnt; i++)
printf("%d ", pr[i]);
return 0;
}
思维有点难度,以后再写
欧拉筛
#include<iostream>
using namespace std;
const int N = 100010;
int pr[N], cnt;
bool st[N];
void GetPrimes(int n) {
for (int i = 2; i < n; i++) {
if (!st[i]) pr[cnt++] = i;
for (int j = 0; i * pr[j] < n; j++) {
st[i * pr[j]] = true;
if (i % pr[j] == 0) break;
}
}
}
int main() {
int n;
cin >> n;
GetPrimes(n);
for (int i = 0; i < cnt; i++)
printf("%d ", pr[i]);
return 0;
}
练习
输入2的每个质因数之和
输入3的每个质因数之和
。。。。。。
输入10000的每个质因数之和