素数筛的时间复杂度是o(nloglogn)无限趋近于o(n)
为什么线性筛时间复杂度是真真正正的o(n),因为完全避免了重复标记
一、基本思想概念
核心思想:用最大的因子来标记合数
有点迷糊?先上概念
约数和定理
:对于一个大于1正整数n可以分解质因数
即任何大于1的整数都可以写成素因子连乘的形式(素因子:因子为素数)
举例:36 =2^2 * 3^2
最大因子M与最小因子P
N = M * P
以18分解为例
2 * 9
3 * 6
6 * 3
9 * 2
9作为最大因子M, 2作为最小因子P,
二,表现形式
9作为最大因子M时:只能标记 18 、27 、36
因为18,才是36 的最大因子,所以36不能标记
三、标记过程
以2~20为例:
假设一个动态数组专门存放素数:array[] = {};
第一次标记:第一个未标记元素自动存入array[], array[] = {2}
即数组中元素作为最小因子P,2~20依次作为最大因子M
关键
:什么时候停止M* P
1:动态数组元素都作为最小P乘过了
2:M%P ==0(下面解释说)
过程一 | array[] = {2},最大因子M=2,最小因子P从array[0]开始遍历 ,即P=2 | M*P可标记———》 |
---|---|---|
过程二 | 3未标记 ,即array[] = {2、3} 最大因子M=3,最小因子P从array[0]开始遍历 | M*P可标记———》 |
过程三 | 4已经被标记 ,没有元素进入动态数组,array[] = {2、3} 最大因子M=4,最小因子P从array[0]开始遍历 | M*P可标记———》 |
当M%P= 0,说明会M中含有=P的因子 即4%2 = 2 ,若 P由2升到3,P就不是最小的因子了,因为4中含有真正的最小因子 | ||
过程四 | 5未标记 ,即array[] = {2、3、5} 最大因子M=5,最小因子P从array[0]开始遍历 | M*P可标记———》 |
过程五 | 6已标记 ,即array[] = {2、3、5} 最大因子M=6,最小因子P从array[0]开始遍历 | M*P可标记———》 |
…… | …… | …… |
过程n | M=20 ,遍历完成, 标记结束 | 输出array[] |
是不是发现M%P=0 的奇妙之处了;下面我们来实现它
四、实现过程(打印100以内的素数)
代码如下:
#include <stdio.h>
#define max_n 100
int prime[max_n +5];
void init(){
for(int i = 2; i <= max_n; i++){ //i相当于最大因子
if(!prime[i]) prime[++prime[0]] = i; //将prime[0]看做一个变量,用以记录动态数组中素数的个数
for(int j = 1;j <= prime[0]; j++) { //prime[j]相当于最小因子
if(prime[j] * i > max_n) break; //退出:相乘超过最大范围
prime[prime[j] * i] = 1;
if(i % prime[j] == 0) break; //最大因子中含有=最小因子的因子,所以表 示最小因子
} //的动态数组素数不能向后遍历了
}
}
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;
}
输出结果: