Eratosthenes篩選法求解質數


問題說明:

除了自身之外,無法被其它整數整除的數稱之為質數,要求質數很簡單,但如何快速的求出質數則一直是程式設計人員與數學家努力的課題, 在這邊介紹一個着名的 Eratosthenes求質數方法。

解法:

首先知道這個問題可以使用回圈來求解,將一個指定的數除以所有小於它的數,若可以
整除就不是質數,然而如何減少回圈的檢查次數?如何求出小於N的所有質數?

我們先來看一個喪心病狂的低效率的解決方式:

//檢驗質數
bool checkZS(int a)
{
    for (int i = 2;i < a;i++)
    {
        if (0 == a%i)
        {
            return false;
        }
    }
    return true;
}

首先我們寫一個檢驗質數的函數,下面我們在主函數調用:

int n = 99999;
    clock_t start,end;//用於計時
    start = clock()    ;
    for(int i = 1;i <= n;i++)
    {
        if (checkZS(i))
        {
            cout<<i<<" ";
        }
    }
    end = clock();
    cout<<"\n總共花費了"<<(long double)(end - start)/CLK_TCK<<""<<endl;

好了,讓我們看下在99999以內的質數算出來的運行結果:

時間花費了17秒,太慢了;下面我們想想怎樣來改進算法!

首先知道這個問題可以使用回圈來求解,將一個指定的數除以所有小於它的數,若可以整除就不是質數,然而如何減少回圈的檢查次數?如何求出小於N的所有質數?
首先假設要檢查的數是N好了,則事實上只要檢查至N的開根號就可以了,道理很簡單,假設A*B = N,如果A大於N的開根號,則事實上在小於A之前的檢查就可以先檢查到這個數可以整除N。 不過在程式中使用開根號會精確度的問題, 所以可以使用 i*i <= N進行檢查, 且執行更快 。
再來假設有一個篩子存放1~N,例如:
2 3 4 5 6 7 8 9 10  11  12 ........N
先將2的倍數篩去:
2 3 5 7 9 11 13........N
再將3的倍數篩去:
2 3 5 7 11 13 17 19........N
再來將5的倍數篩去,再來將7的質數篩去,再來將11的倍數篩去........,如此進行到最后留下的數就都是質數,這就是Eratosthenes篩選方法(Eratosthenes Sieve Method)

 檢查的次數還可以再減少,事實上,只要檢查6n+1與6n+5就可以了,也就是直接跳過2與3的倍
數,使得程式中的if的檢查動作可以減少。

下面我們上代碼:

/*
問題:
除了自身之外,無法被其它整數整除的數稱之為質數,要求質數很簡單,但如何快速的
求出質數則一直是程式設計人員與數學家努力的課題, 在這邊介紹一個着名的 Eratosthenes求質
數方法。
2013/7/18
張威
*/
#include <iostream>
#include <time.h>
using namespace std;

#define n 99999

int main()
{
    int a[n+1];//建立一個數組,使a[i] == i,這樣通過篩選,將非質數所在位置置0
    for (int i = 0;i <= n;i++)
    {
        a[i] = i;
    }
    clock_t start,end;//用於計時
    start = clock()    ;

    //每次進行篩選的數,進行優化,實際上只要篩選到 N開放就行
    for (int i = 2;i*i <= n;)
    {
        //從i處開始篩選(比i小的肯定不能被i整除)
        for (int j = i;j <= n;j++)
        {
            //通過while循環.跳過中間置0區域
            while(0 == a[j] && j <= n)
                {
                    j++;
                }
            //假如a[j]能被i整除而且不相等(也就是說不是本身),就把這個位置數值置為0
            if (0 == a[j]%i && i != a[j])
            {
                a[j] = 0;
            }
        }
        //i的步進值優化,即跳過2或3的倍數,每次遞增數加大
        if((i-1)%6 == 0)
            i += 4;
        else if((i-5)%6 == 0)
        {
            i += 2;
        }
        else
        {
            i++;
        }
    }
    end = clock();
    for(int i = 2;i <= n;i++)
    {
        if (a[i] != 0)
        {
            cout<<a[i]<<" ";
        }
    }
    cout<<"\n總共花費了"<<(long double)(end - start)/CLK_TCK<<""<<endl;
    return 1;
}
Eratostheness

上面標出了在減少算法中循環次數的優化方面所進行的修改,下面是運行結果:

兩者之間的差距的話.......不說了,自己寫的東西和這些算法相比就是渣渣!

下面上示例上面的代碼:

#include <stdio.h>
#include <stdlib.h>
#define N 1000
int main(void) {
int i, j;
int prime[N+1];
for(i = 2; i <= N;i++)
prime[i] = 1;
for(i = 2; i*i <= N;i++) { // 這邊可以改進
if(prime[i] == 1) {
for(j = 2*i; j <= N;j++) {
if(j % i == 0)
prime[j] = 0;
}
}
}
for(i = 2; i < N;i++) {
if(prime[i] == 1) {
printf("%4d ", i);
if(i % 16 == 0)
printf("\n");
}
}
printf("\n");
return 0;
}
示例代碼


可以看到其實還是上面自己寫的在示例的基礎上還是做了些改善的!


免責聲明!

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



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