C#找質數(素數)厄拉多塞篩法
質數(prime number)又稱素數,有無限個。指整數在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數。換句話說,只有兩個正因數(1和自己)的自然數即爲素數(除了1和它本身以外不再有其他的因數)。根據算術基本定理,每一個比1大的整數,要麼本身是一個質數,要麼可以寫成一系列質數的乘積,比1大但不是素數的數稱爲合數。1和0既非素數也非合數。最小的質數是2。
厄拉多塞(Eratosthenes, 公元前276–前196)古希臘天文學家。他是阿基米德的朋友,也和亞里士多德一樣是一個具有廣泛興趣的人。他不僅是著名的天文學家和數學家,而且還是地理學家、歷史學家,甚至還涉獵文學評論。他在數學方面他研究出一個素數系統,現在叫做厄拉多塞篩法(Eratosthenes sieve),是一種從按照順序排列的所有自然數中找出素數的方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConAppPrimeNumberOther
{
class Program
{
static void Main(string[] args)
{
var s = new Stopwatch();
s.Start();
PrimeNumber.Find();
s.Stop();
System.Console.WriteLine("Watch: {0}ms",s.ElapsedMilliseconds);
s.Reset();
s.Start();
PrimeNumber.Find2();
s.Stop();
System.Console.WriteLine("Watch: {0}ms", s.ElapsedMilliseconds);
s.Reset();
s.Start();
PrimeNumber.Find3();
s.Stop();
System.Console.WriteLine("Watch: {0}ms", s.ElapsedMilliseconds);
s.Reset();
s.Start();
PrimeNumber.Find4();
s.Stop();
System.Console.WriteLine("Watch: {0}ms", s.ElapsedMilliseconds);
}
}
public class PrimeNumber
{
const int N = 1000000;
//const int N = 1000000;
//static int count = 0;
/*
一.經典算法
經典的素數判定算法是這樣:給定一個正整數n,用2到sqrt(n)之間的所有整數去除n,如果可以整除,則n不是素數,如果不可以整除,則n就是素數。所以求小於N的所有素數程序如下:
*/
public static void Find()
{
int m, n;
int count = 0;
for (n = 2; n < N; n++)
{
for (m = 2; m * m <= n; m++)
if (n % m == 0) break;
if (m * m > n)
{
count++;
//System.Console.Write("{0} \t", n);
}
}
System.Console.Write("count: {0} \n", count);
}
/*
算法的時間複雜度是O(N*sqrt(N)),求解規模較小的輸入,尚且沒有問題。但對於規模較大的N,算法就力不從心了。有一種算法叫厄拉多塞篩(sieve of Eratosthenes),它在求解時具有相當高的效率,但是要犧牲較多的空間。
*/
/*
二.厄拉多塞篩算法
這個程序的目標是,若i爲素數,則設置a[i] = 1;如果不是素數,則設置爲0。首先,將所有的數組元素設爲1,表示沒有已知的非素數。然後將已知爲非素數(即爲已知素數的倍數)的索引對應的數組元素設置爲0。如果將所有較小素數的倍數都設置爲0之後,a[i]仍然保持爲1,則可判斷它是所找的素數。
*/
public static void Find2()
{
int []a = new int [N];
int i, j;
int count = 0;
for (i = 2; i < N; i++) a[i] = 1;
for (i = 2; i < N; i++)
{
if (Convert.ToBoolean(a[i]))
//if (a[i]==1)
for (j = i + i; j < N; j += i)
a[j] = 0;
}
for (i = 2; i < N; i++)
if (Convert.ToBoolean(a[i]))
//if (a[i] == 1)
{
count++;
//System.Console.Write("{0} \t", i);
}
System.Console.Write("count: {0} \n", count);
}
/*
例如,計算30以內的素數,先將所有數組項初始化爲1(如下第二列),表示還每發現非素數的數。接下來,將索引爲2、3、5倍數的數組項設置成0,因爲2、3、5倍數的數是非素數。保持爲1的數組項對應索引爲素數(如下最右列)。
i 2 3 5 a[i]
2 1 1
3 1 1
4 1 0
5 1 1
6 1 0
7 1 1
8 1 0
9 1 0
10 1 0
11 1 1
12 1 0
13 1 1
14 1 0
15 1 0
16 1 0
17 1 1
18 1 0
19 1 1
20 1 0
21 1 0
22 1 0
23 1 1
24 1 0
25 1 0
26 1 0
27 1 0
28 1 0
29 1 1
30 1 0
如何理解厄拉多塞篩算法呢?
我們一定記得小學時候就學過的素數和合數的性質:任何一個合數一定可以表示成若干個素數之積。如:4 = 2 * 2,6 = 2 * 3,12 = 2 * 2 * 3。也就是說,合數N一定是小於N的某個(或若干)素數的整數倍,反之,如果N不是任何比它小的素數的倍數,則N必然是素數。
*/
/*
三.經典算法的改進版本
經典素數判定算法中,並不需要用2到sqart(n)之間的所有整數去除n,只需要用其間的素數就夠了,原因也是合數一定可以表示成若干個素數之積。算法的改進版本如下:
*/
public static void Find3()
{
int i, n, flag;
int []prime=new int[N];
prime[0] = 0; //保存素數的總個數
int count = 0;
for (n = 2; n < N; n++)
{
flag = 1;
for (i = 1; i <= prime[0] && prime[i] * prime[i] <= n; i++)
if (n % prime[i] == 0)
{
flag = 0;
break;
}
if (Convert.ToBoolean(flag))
//if (flag == 1)
{
prime[0]++;
prime[prime[0]] = n;
count++;
//System.Console.Write("{0} \t", n);
}
}
System.Console.Write("count: {0} \n", count);
}
/*
算法雖然在效率下,比先前版本有所提高,但需要犧牲空間記住已確定的素數。
*/
/*
四.厄拉多塞篩算法的改進版
程序1-2使用一個數組來包含最簡單的元素類型,0和1兩個值,如果我們使用位的數組,而不是使用整數的數組,則可獲得更高的空間有效性。
*/
//const N 1000000;
//static int BITSPERWORD = 2;
//const int SHIFT = 1;
//const int MASK = 0x1;
//static int BITSPERWORD = 4;
//const int SHIFT = 2;
//const int MASK = 0x3;
//static int BITSPERWORD = 8;
//const int SHIFT = 3;
//const int MASK = 0x7;
//static int BITSPERWORD = 16;
//const int SHIFT = 4;
//const int MASK = 0xF;
static int BITSPERWORD = 32;
const int SHIFT = 5;
const int MASK = 0x1F;
//static int BITSPERWORD = 64;
//const int SHIFT = 6;
//const int MASK = 0x3F;
//static int BITSPERWORD = 128;
//const int SHIFT = 7;
//const int MASK = 0x7F;
//static int BITSPERWORD = 256;
//const int SHIFT = 8;
//const int MASK = 0xFF;
static int COUNT = 1+N/BITSPERWORD; //Bit i 對映數i
public static void Find4()
{
int i, j;
int []a= new int [COUNT];
int count = 0;
for (i = 0; i < COUNT; i++) a[i] = -1;
for (i = 2; i < N; i++)
{
if (Convert.ToBoolean(test(a, i)))
//if (!(test(a, i) == 0))
{
for (j = i + i; j < N; j += i)
{
clr(a, j);
//System.Console.Write("[" + clr2(a, j) + "]");
}
//System.Console.Write("[" + test(a, i) + "]");
}
}
for (i = 2; i < N; i++)
{
if (Convert.ToBoolean(test(a, i)))
//if (!(test(a, i) == 0))
{
//System.Console.Write("["+test(a, i)+"]");
count++;
//System.Console.Write("{0} ", i);
}
}
System.Console.Write("count: {0} of {1} \n", count,N);
}
static void set(int []a, int i)
{
a[i >> SHIFT] |= (1 << (i & MASK));
}
static void clr(int []a, int i)
{
a[i >> SHIFT] &= ~(1 << (i & MASK));
}
static int test(int []a, int i)
{
return a[i >> SHIFT] & (1 << (i & MASK));
}
static int set2(int[] a, int i)
{
return a[i >> SHIFT] |= (1 << (i & MASK));
}
static int clr2(int[] a, int i)
{
return a[i >> SHIFT] &= ~(1 << (i & MASK));
}
}
}
版權所有,轉載請註明文章出處 http://blog/csdn.net/cadenzasolo