线性筛法(一)--素数筛法(一)

目录

筛法

所谓筛法是一种思想,就像名字一样,筛去多余的,筛去错误的。多数情况用数组标记,复杂度看起来很大,但代码跑起来确是越跑越快。

素数筛法

问题引入

把n以内素数全找出来(n<=100000)

大家一定想得到第一种方法,暴力,遍历。

//暴力
#include<stdio.h>
#include<math.h>
int isprime(int n)
{
	n = sqrt(n);
	for(int i = 2; i < n; i++)
	{
		if(n%i==0)
			return 0;
	}
	return 1;
}
int main()
{
	scanf("%d",&n);
	for(int i = 1; i <= n; i++)
	{
		if(isprime(n))
			printf("%d ",i);
	}
	return 0;
}

注:用n=sqrt(n),可以增加运算速度,毕竟sqrt也是很慢的。但是这种方法还是很慢,慢在每次都要判断。

接下来看筛法

代码分析

所谓素数,就是不能表示为比它更小的数的乘积。
且,任何一个素数的倍数一定不是素数。

我们先定义一个数组来存100000以内数是否的素数,下标表示数,数组的值1表示是素数,0表示不是素数。

我们可以换个角度思考,要找素数,其实只要把不是素数的排除就可以了。

接下来我们可以构造一张表。
初始

0 1 2 3 4 5 6 7 8 9 10
0 0 0 0 0 0 0 0 0 0 0

我们可以把2的倍数变成1,因为任何一个素数的倍数一定不是素数。

0 1 2 3 4 5 6 7 8 9 10
0 0 1 0 1 0 1 0 1 0 1

然后是3的倍数

0 1 2 3 4 5 6 7 8 9 10
0 0 1 0 1 0 1 0 1 1 1

。。。。。。

照这样就可以得到一张素数表了,哈哈。
看代码。

//筛选法
#include<stdio.h>
#include<math.h>
int a[100001];
int main()
{
	int n;
	while(scanf("%d",&n) == 1)
	{
		for(int i = 2; i <= n; i++)
		{
			if(a[i] == 0)
			{
				for(int j = i*2; j < n; j+=i)
					a[j] = 1;
			}
		}
		printf("2");
		for(int i = 3; i <= n; i++)
		{
			if(a[i] == 0)
			printf(" %d",i);	
		}
	}
	reutrn 0;
}

这个代码还可以改进。
第一,我们不用筛到n,其实只要和暴力方法一样,筛到sqrt(n)就可以了。
第二,我们筛3时多筛了3*2,筛5时多筛了5*2,5*3,5*4。只要从i*i开始筛就可以了。

//筛选法优化一
#include<stdio.h>
#include<math.h>
int a[100001];
int main()
{
	int n;
	while(scanf("%d",&n) == 1)
	{
		n = sqrt(n);
		for(int i = 2; i <= n; i++)
		{
			if(a[i] == 0)
			{
				for(int j = i*i; j < n; j+=i)
					a[j] = 1;
			}
		}
		printf("2");
		for(int i = 3; i <= n; i++)
		{
			if(a[i] == 0)
			printf(" %d",i);	
		}
	}
	reutrn 0;
}

其实还可以改进,这里我就不说了。
把2的情况用位运算,再把j+=i变为j+=2*i

总结

看起来时间复杂度为O(n*n),其实在处理很多数据时,它是越跑越快,相当于O(6*n)。

另外

1,如果要判断素数个数,可以用一个数组存一下有几个,结合筛法,也是很方便的。
2,如果要判断50-100有几个素数,只要(100的素数个数-50的素数个数),再判断一下100与50的素数情况。
3,这种筛法叫埃拉托斯特尼筛法。

下一篇:线性筛法(一)--素数筛法(一)

原文地址:https://www.cnblogs.com/juicebox/p/9644482.html