C#找質數(素數)厄拉多塞篩法

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章