1. 程式人生 > >素數篩選法 NITOJ--127

素數篩選法 NITOJ--127

這道題乍一看非常簡單,就是判斷素數而已,但是實際上,由於題目限制的時間只有100ms,所以普通的方法很容易超時。

這裡需要用到素數篩選法。

素數篩選法適用於求不大於給定數n的素數。

核心思想就是,首先假設從2到n所有的數都是素數,然後從2開始遍歷,把所有2的倍數全部標記為非素數,接著找下一個沒有被標記的(素)數,也就是3,同樣把3的倍數全部標記為非素數。重複上述步驟,找到5,標記5的倍數,找到7,標記7的倍數。最後剩下的數就全部都是素數了。

貼一段素數篩選法的程式碼:

memset(prime,true,sizeof(prime));
	prime[2]=true;
	for(i=3;i*i<=100001;i+=2)
	{
		if(prime[i])
		{
			for(j=2*i;j<=100001;j+=i)
			{
				prime[j]=false;
			}
		}
	}

這段程式碼已經經過一些小優化,由於除了2以外,其他的素數都是奇數,所以可以直接遍歷所有奇數,這樣效率就提高了一倍。

素數篩選法就這麼簡單,接下來額外介紹一個和本題無關的快速判斷一個數是不是素數的演算法。

根據孿生素數猜想,除了2和3以外,其他的素數全部分佈在6的倍數週圍,比如5(6)7,11(12)13,17(18)19。

所以可以下這樣的結論,或者說猜想:除了2和3外,所有的素數都與6的倍數相鄰,但是與6的倍數相鄰的,不一定是素數。

有了這個猜想,我們就只需要判斷每個6的倍數週圍的數是不是素數就可以了。

上程式碼

int Prime(int n)
{
	int i;
	if(n==3 || n==5)
		return 1;
	if(n%6!=1 && n%6!=5)
		return 0;
	for(i=5;i<=sqrt(n);i+=6)
		if(n%i==0 || n%(i+2)==0)
			return 0;
	return 1;
}


最後還有這道題的ac程式碼

#include<stdio.h>
#include<string.h>

bool prime[100001];
int num[10000];

int main()
{
	int i,j,k,n,ans,flag;
	int left,right,mid;
	num[0]=2;
	k=1;
	memset(prime,true,sizeof(prime));
	for(i=3;i*i<=100001;i+=2)
	{
		if(prime[i])
		{
			for(j=2*i;j<=100001;j+=i)
			{
				prime[j]=false;
			}
		}
	}
	for(i=3;i<=100001;i+=2)
	{
		if(prime[i])
			num[k++]=i;
	}
	while(scanf("%d",&n)!=EOF)
	{
		flag=0;
		ans=0;
		left=0;right=k-1;
		while(left<right)
		{
			if(n>num[right])
			{
				flag=right+1;
				break;
			}
			mid=(left+right)/2;
			if(num[mid]>n)
			{
				if(num[mid-1]<=n)
				{
					flag=mid;
					break;
				}
				else
				{
					right=mid-1;
				}
			}
			else if(num[mid]==n)
			{
				flag=mid+1;
				break;
			}
			else if(num[mid]<n)
			{
				if(num[mid+1]>n)
				{
					flag=mid+1;
					break;
				}
				else if(num[mid+1]==n)
				{
					flag=mid+2;
					break;
				}
				else
				{
					left=mid+1;
				}
			}
		}
		printf("%d\n",flag);
	}
	return 0;
}

這道題除了需要用素數篩選法以外,還需要打表後用二分查詢,要不然會超時。