总体思路:用素数来标记合数
具体步骤:
1、用prime[i]来标记i是否是合数
2、标记为1的数字为合数,否则为素数
3、第一次知道2是素数,则将2的倍数标记为1
4、向后找到第一个没有被标记的数字i
5、将i的倍数全部标记为合数
6、重复4–6步,直到标记完范围内的所有数字
代码如下:
#include <stdio.h>
#define max_n 100
int prime[max_n + 5] = {0};
void init() {
for(int i = 2; i <= max_n; i++) {
if(prime[i]) continue; //若为1 即不是素数,退出本次循环
prime[++prime[0]] = i; //prime[0] 等同一个变量 将其看做++n,已经初始化n = 0,记录数据有多少个
for(int j = i*i; j<= max_n; j += i) { //改2 * i 为 i * i
prime[j] = 1; //、所以数组大小不能超过10 0000、
} //i * i超过整型的极大值,就表示为负数
}
return ;
}
int main() {
init();
for (int i = 1; i <= prime[0]; i++){
printf("%d\t",prime[i]);
if(i % 5 == 0) puts("\n"); //每5个数据换行输出
}
puts("\n");
printf("prime[0] = %d\n",prime[0 ]);
return 0;
}
疑问:为什么素数筛的时间复杂度是0(loglongN),为何不是0(n^2)
da:总认为两层循环嵌套就是0(n^2),这是错误的思想,要看循环频率,外层循环一次,内层循环多少次,该方法外层循环一次,内层循环longlongN次,具体怎么算的是数论的东西,可以看这里埃拉托色尼筛法,经过改进又被称为线性时间筛法
即大家认为的o(n)时间复杂度(这里素数筛可看作约等于o(n))
函数的优化:
初始:
for(int i = 2; i <= max_n; i++) {
if(prime[i]) continue; //如果不是素数,推出本次循环
for(int j = 2*i; j<= max_n; j += i) {
prime[j] = 1;
}
}
值得注意的是:一个小优化:避免了重复标记
for(int j = i*i; j<= max_n; j += i) { //改2 * i 为 i * i
prime[j] = 1; //、所以数组大小不能超过10 0000、
} //i * i超过整型的极大值,就表示为负数
再次优化:当i= 10 0000 时避免i * i的超出整型最大值
for(int j = i; j<= max_n / i; j ++) { //改2 * i 为 i * i
prime[j * i] = 1;
}