1. 程式人生 > >一個空間換時間演算法

一個空間換時間演算法

說起空間換時間,想到c/c++語言的話我會想到#define巨集定義和行內函數,他們都減少了函式切換時的壓棧清棧等工作.對一個簡短的函式,的確這些額外的消耗太浪費了.對於"計算素數"的問題,是一個經典的此類問題.

我們常用來找一個範圍內的素數(質數)的辦法有兩種:
(1)篩選法
(2)判定法,即定義法

如:求1100間的所有素數。
分析

用篩選法,先把2100的數存到一個數組中,然後先把2的所有倍數刪除掉(即讓此數變為0),再刪3的倍數,繼續往上就是5的倍數,7的倍數……,最後,剩下的數(即陣列中不為0的數)就是素數。

用判定法,對2-100內的每個數,根據素數的定義,除本身外沒有其它約數來判定是否為素數.這個有個注意的地方是:找約數的時候不用找到n,也不用找到n/2,只要到sqrt(n)就行了.[別問為什麼?自己想去咯.] 具體是<?, 這裡有int 和double 的比較,有寫sqrt(n)+1,也看到有人寫sqrt(n+1) 的,哪個是正解自己多思考呵.

驗證:
以下是篩選法程式碼:
long
 filter(int end)  //method:  filter  1..end{
    start 
= time(NULL);
    
    
long count =0;
    
long len = end +1;
    
int i,j,k;

    
if(end<2return0;

    
//生成篩選集int* p =newint[len];
    
if(p == NULL) return0;
    
for(i=0; i<len; ++i){
        p[i] 
= i;
    }
    
    
    
//篩選演算法    k =2;    //作為篩選的除數
    i = k;
    
int max_test = sqrt(end) +1;
    
while(i< max_test){
        
for(j=k+1; j<len; ++j)
        {
            
if(0== p[j])
                
continue;
            
elseif(p[j] % i ==0) {
                p[j] 
=0;
            }
                
        }
        
for(j=k+1;j<len;++j)
        {
            
if(p[j] !=0){
                k 
= j;
                
break;
            }
        }
        i 
= k;
    }


    
//列印結果for(i=2;i<end;++i)
        
if(p[i] !=0
        {
            count
++;
            
//printf("%d\t",p[i]);        }
    end 
= time(NULL);

    printf(
"\nit takes your %f seconds in filter.\n",difftime(end,start));
    printf(
"filter(end): count = %ld",count);
    delete[] p;


    
return count;

}
以下是判定法程式碼:
long judge(int beg, int end)  //method: judge every number{
    start 
= time(NULL);
    
int count =0;

    
if(beg>end || beg<1return0;
    
    register 
int i;
    
while(beg<end+1)
    {
        
for(i=2;i<sqrt(beg)+1++i){
            
if(beg % i ==0break;
        }
        
if(i>sqrt(beg)) 
        {
//    printf("%d\t",beg);            count++;
            
//NULL;        }
        beg
++;

    }

        
    end 
= time(NULL);

    printf(
"\nit takes your %f seconds in judge.\n",difftime(end,start));
    printf(
"judge(end): count = %ld\n",count);
    
return count;
}

下面看我們的結果:
我測試了從1-1000000內的素數:  耗時和統計結束如下:

it takes your 2.000000 seconds in filter.
filter(end): count = 78498
it takes your 5.000000 seconds in judge.
judge(end): count = 78498
Press any key to continue

這說明在N很大時,篩選法體現出了它的高效.在N比較小時,則看不出來其明顯優勢咯.篩選法用了很多的記憶體放要被處理的資料.但是此演算法對記憶體的訪問是順序的,在經過有選擇的取出除數(篩選器?)來否定一些保留一些.經過相對比較少的次數完成了對全部資料的篩選工作.在演算法複雜度上,儘管還是O(n^2)[與判定法沒有什麼區別],但事實在其執行次數和訪問速度得到了很大的提高.

當然,以上的篩選演算法還是可以再改進一下的.
long filter2(int end)  //method:  filter  1..end{
    start 
= time(NULL);
    
    
long count =0;
    
long len = (end +1)/2;
    
int i,j,k;

    
if(end<2return0;

    
//生成篩選集int* p =newint[len];
    
if(p == NULL) return0;
    
for(i=0; i<len; ++i){
        p[i] 
= i*2+1;
    }
    
    
    
//篩選演算法    k =1;    //作為篩選的除數在陣列中的下標    i = k;
    
int max_test = sqrt(end) +1;
    
while(p[i]< max_test){
        
for(j = k+1;j<len;++j)
        {
            
if(0== p[j])
                
continue;
            
elseif(p[j] % p[i] ==0) {
                p[j] 
=0;
            }
                
        }
        
for(j=k+1;j<len;++j)
        {
            
if(p[j] !=0)
            {
                k 
= j;
                
break;
            }
        }
        i 
= k;
    }


    
//列印結果for(i=1;i<len;++i)
        
if(p[i] !=0
        {
            count
++;
        
//    printf("%d\t",p[i]);        }
    end 
= time(NULL);

    printf(
"\nit takes your %f seconds in filter.\n",difftime(end,start));
    printf(
"filter2(end): count = %ld",count);
    delete[] p;


    
return count;

}

以上的演算法沒有實質改進,不過其所用的記憶體少了一半,在進行比較等操作時迴圈也只有原來一半,所用的總時間和上面的篩選演算法比也只是一半多一點,我覺得最重要的是,申請的記憶體少了,函式失敗可能性變少,而且這個改進是比較有必要的,若任由它作無謂的計算,我會很心痛咯.呵呵.

注:雖然因判定法只用了幾個變數,我想宣告為暫存器變數,可是你知道的,"儘可能"並不是"一定",何況即使得到了,還不夠快.硬體和軟體演算法的改善都有效果,硬體要成本,演算法要技術。怎麼辦?掠拌.