小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

從關(guān)于素數(shù)的算法題來學(xué)習(xí)如何提高代碼效率

 蔣大培 2013-12-06

今天在博文C語言初學(xué)者代碼中的常見錯誤與瑕疵(5)看了一個關(guān)于素數(shù)的算法題,如下:

素數(shù)

在世博園某信息通信館中,游客可利用手機等終端參與互動小游戲,與虛擬人物Kr. Kong 進(jìn)行猜數(shù)比賽。

當(dāng)屏幕出現(xiàn)一個整數(shù)X時,若你能比Kr. Kong更快的發(fā)出最接近它的素數(shù)答案,你將會獲得一個意想不到的禮物。

 

例如:當(dāng)屏幕出現(xiàn)22時,你的回答應(yīng)是23;當(dāng)屏幕出現(xiàn)8時,你的回答應(yīng)是7;

若X本身是素數(shù),則回答X;若最接近X的素數(shù)有兩個時,則回答大于它的素數(shù)。

 

輸入:第一行:N 要競猜的整數(shù)個數(shù)

接下來有N行,每行有一個正整數(shù)X

輸出:輸出有N行,每行是對應(yīng)X的最接近它的素數(shù)

 

樣例:輸入

4

22

5

18

8

輸出

23

5

19

7

 

看到這個算法題我們首先要做的就是實現(xiàn)一個函數(shù),來求出一個數(shù)是否是質(zhì)數(shù)。下面我們來簡單的實現(xiàn)一下:

復(fù)制代碼
bool isPrime(int num)
{
    if(num < 2) return false;
    for(int i=2; i*i<num; ++i){
        if(num % i == 0) return false;
    }
    return true;
}
復(fù)制代碼


由于這個函數(shù)在算法中會多次用到,我們用下面的測試來查看這個基本函數(shù)的效率

復(fù)制代碼
void test(){
    clock_t start = clock();
    for(int i=1; i <= 100000; ++i){
        isPrime(1000000007);
    }
    clock_t end = clock();
    cout << endl << static_cast<double>(end - start)/CLOCKS_PER_SEC << endl;
}
復(fù)制代碼

運行,得到結(jié)果12.158

因為期間我們可能會進(jìn)行重復(fù)的計算,對這個問題我一開始想到的解決方法就是建立一個質(zhì)數(shù)表,我們可以直接通過查找表來快速的確定一個數(shù)是否是質(zhì)數(shù)。當(dāng)要判斷的數(shù)很大時,需要占用很大的空間來建表,為了節(jié)約空間,我將每一位都充分用上了。

復(fù)制代碼
#define GETNUM(x) psum[((x)>>3)]&(1<<(((x)&7)-1))
#define SETNUM(x) psum[((x)>>3)] &= (~(1<<(((x)&7)-1)))
bool isPrime(int num)
{
    if(num < 2) return false;
    int size = (num>>3)+1;
    unsigned char *psum = new unsigned char[size];
    memset(psum, 0xFF, size);
    for(int i=2; i*i<num; ++i){
        if(GETNUM(i)){
            for(int j=i<<1; j<=num; j+=i){
                SETNUM(j);
            }
        }
    }
    bool result = GETNUM(num);
    delete [] psum;
    return result;
}
復(fù)制代碼


因為質(zhì)數(shù)表建立起來以后,之后的判斷直接取值就行了,所以我們就不做循環(huán)了,直接看它運行一次的時間,竟然用了29.853!耗時太長了,建這個表的時間可以進(jìn)行20萬次試除法判斷了。

在經(jīng)過一定的分析后,我將這個過程進(jìn)行了一下優(yōu)化

復(fù)制代碼
#define GETNUM(x) psum[((x)>>3)]&(1<<(((x)&7)-1))
#define SETNUM(x) psum[((x)>>3)] &= (~(1<<(((x)&7)-1)))
bool isPrime2(int num)
{
    if(num < 2) return false;
    int size = (num>>3)+1;
    unsigned char *psum = new unsigned char[size];
    memset(psum, 0xFF, size);
    for(int j=4; j<num; j+=2){
        SETNUM(j);
    }
    for(int i=3; i*i<num; ++i){
        if(GETNUM(i)){
            int step = i<<1;
            for(int j=i*i; j<=num; j+=step){
                SETNUM(j);
            }
        }
    }

    bool result = GETNUM(num);
    delete [] psum;
    return result;
}
復(fù)制代碼

上面的優(yōu)化,我先是直接將2的倍數(shù)都淘汰掉,接著,基于在進(jìn)行i的倍數(shù)判斷時,所有i的i-1以下的倍數(shù)都已經(jīng)被淘汰掉了這一點,直接從i的平方開始淘汰,而且基于偶數(shù)倍能被2整除這一點,將步長調(diào)整為i*2.

經(jīng)過優(yōu)化,時間縮短為16.429,可是這個結(jié)果明顯是不能讓人滿意滴。。。

這時我參看了一下博文一個超復(fù)雜的間接遞歸——C語言初學(xué)者代碼中的常見錯誤與瑕疵(6),發(fā)現(xiàn)只是計算部分質(zhì)數(shù)表,再利用質(zhì)數(shù)表來加快質(zhì)數(shù)的試除法這個方案很有可行性,于是趕緊行動。先進(jìn)行預(yù)算,再進(jìn)行試除法判斷質(zhì)數(shù)。

View Code


改進(jìn)的結(jié)果是令人振奮滴,時間縮短為0.021.

解決了素數(shù)判斷問題,得到想要的算法就很容易了

復(fù)制代碼
void precalc(int size, int * primes, int &pnum)
{
    bool *psum = new bool[size+1];
    for(int j=4; j<=size; j+=2){
        psum[j] = false;
    }
    memset(primes, 0, size * sizeof(int));
    primes[0] = 2;
    pnum = 1;
    int i=3;
    for(; i*i<=size; ++i){
        if(psum[i]){
            primes[pnum] = i;
            ++pnum;
            int step = i<<1;
            for(int j=i*i; j<=size; j+=step){
                psum[j] = false;
            }
        }
    }
    for(;i<=size; ++i){
        if(psum[i]){
            primes[pnum] = i;
            ++pnum;
        }
    }
    delete [] psum;
}
bool isPrime5(int num, const int * primes, int pnum)
{
    for(int i=0; i<pnum && primes[i]*primes[i] < num; ++i){
        if(num % primes[i] == 0) return false;
    }
    return true;
}
int get_nearest(int num, const int * primes, int pnum)
{
    if(isPrime5(num, primes, pnum)) return num;
    int len;
    if(num % 2 == 0){
        len = 1;
    }
    else{
        len = 2;
    }
    while(true){
        if(isPrime5(num+len, primes, pnum)) return num + len;
        if(isPrime5(num-len, primes, pnum)) return num - len;
        len += 2;
    }
}
  
int _tmain(int argc, _TCHAR* argv[])
{
    cout<<"請輸入數(shù)據(jù)"<<endl;
    int count;
    cin>>count;
    int *data = new int[count];
    //最大的一個數(shù)
    int maxnum = 0;
    for(int i=0; i<count; i++){
        cin>>data[i];
        if(maxnum < data[i]){
            maxnum = data[i];
        }
    }
    int size = static_cast<int>(sqrt(static_cast<double>(maxnum)));
    int *primes = new int[size];
    int pnum;
    precalc(size, primes, pnum);
    for(int i=0; i<count; i++){
        cout<<get_nearest(data[i], primes, pnum)<<endl;
    }
    delete [] primes;
    delete [] data;
    cin.get();
    return 0;
}
復(fù)制代碼

 

 

 

 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多