数论 HihoCoder1295 Eular质数筛法

#1295 : 数论二·Eular质数筛法

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Ho:小Hi,上次我学会了如何检测一个数是否是质数。于是我又有了一个新的问题,我如何去快速得求解[1,N]这个区间内素数的个数呢?

小Hi:你自己有什么想法么?

小Ho:有!我一开始的想法是,自然我们已经知道了如何快速判定一个数是否是质数,那么我就直接将[1,N]之间每一个数判定一次,就可以得到结果。但我发现这个方法太笨了。

小Hi:确实呢,虽然我们已经通过快速素数检测将每一次判定的时间复杂度降低,但是N个数字的话,总的时间复杂度依旧很高。

小Ho:是的,所以后来我改变了我的算法。我发现如果一个数p是质数的话,那么它的倍数一定都是质数。所以我建立了一个布尔类型的数组isPrime,初始化都为true。我从2开始枚举,当我找到一个isPrime[p]仍然为true时,可以确定p一定是一个质数。接着我再将N以内所有p的倍数全部设定为isPrime[p*i]=false。

写成伪代码为:

isPrime[] = true
primeCount = 0
For i = 2 .. N
	If isPrime[i] Then
		primeCount = primeCount + 1
		multiple = 2
		While (i * multiple ≤ N)
			isPrime[i * multiple] = false
			multiple = multiple + 1
		End While 
	End If
End For
  

小Hi:小Ho你用的这个算法叫做Eratosthenes筛法,是一种非常古老的质数筛选算法。其时间复杂度为O(n log log n)。但是这个算法有一个冗余的地方:比如合数10,在枚举2的时候我们判定了一次,在枚举5的时候我们又判定了一次。因此使得其时间复杂度比O(n)要高。

小Ho:那有没有什么办法可以避免啊?

小Hi:当然有了,一个改进的方法叫做Eular筛法,其时间复杂度是O(n)的。

提示:Eular质数筛法

输入

第1行:1个正整数n,表示数字的个数,2≤n≤1,000,000。

输出

第1行:1个整数,表示从1到n中质数的个数

样例输入

9

样例输出

4

 

质数的线性筛法:

bool型prime数组标记是否为质数,p[]数组动态记录质数,从小到大。

每个数都遍历一遍已经得到的质数(小于它的质数),2*3=6,3*2=6,这样子会重复,所以遍历到某个质数是当前数字的质因子时,便可跳出循环,(因为肯定有更大的数字j大于当前i,使该质数*j筛去i能筛去的数),能够使确保每个合数都是被它的最小质因数筛去。

代码实现:

void Eular()
{
	ans=0;
	memset(p,0,sizeof(p));
	for(int i=2;i<=n;++i) prime[i]=true;
	for(int i=2;i<=n;++i){
		if(prime[i])
			p[ans++]=i;
		for(int j=0;j

 

题目完整代码:

#include 
#include 

const int maxn=1000020;

int n;
bool prime[maxn];
int p[maxn],ans;

void Eular()
{
	ans=0;
	memset(p,0,sizeof(p));
	for(int i=2;i<=n;++i) prime[i]=true;
	for(int i=2;i<=n;++i){
		if(prime[i])
			p[ans++]=i;
		for(int j=0;j

 

你可能感兴趣的:(ACM,数论)