線性篩法(一)--素數篩法(一)


目錄

篩法

所謂篩法是一種思想,就像名字一樣,篩去多余的,篩去錯誤的。多數情況用數組標記,復雜度看起來很大,但代碼跑起來確是越跑越快。

素數篩法

問題引入

把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,這種篩法叫埃拉托斯特尼篩法。

下一篇:線性篩法(一)--素數篩法(一)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM