一個空間換時間演算法
阿新 • • 發佈:2019-02-12
說起空間換時間,想到c/c++語言的話我會想到#define巨集定義和行內函數,他們都減少了函式切換時的壓棧清棧等工作.對一個簡短的函式,的確這些額外的消耗太浪費了.對於"計算素數"的問題,是一個經典的此類問題.
我們常用來找一個範圍內的素數(質數)的辦法有兩種:
(1)篩選法
(2)判定法,即定義法
如:求1—100間的所有素數。
分析:
用篩選法,先把2—100的數存到一個數組中,然後先把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<2) return0;
//生成篩選集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<1) return0;
register int i;
while(beg<end+1)
{
for(i=2;i<sqrt(beg)+1; ++i){
if(beg % i ==0) break;
}
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<2) return0;
//生成篩選集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;
}
以上的演算法沒有實質改進,不過其所用的記憶體少了一半,在進行比較等操作時迴圈也只有原來一半,所用的總時間和上面的篩選演算法比也只是一半多一點,我覺得最重要的是,申請的記憶體少了,函式失敗可能性變少,而且這個改進是比較有必要的,若任由它作無謂的計算,我會很心痛咯.呵呵.
注:雖然因判定法只用了幾個變數,我想宣告為暫存器變數,可是你知道的,"儘可能"並不是"一定",何況即使得到了,還不夠快.硬體和軟體演算法的改善都有效果,硬體要成本,演算法要技術。怎麼辦?掠拌.
我們常用來找一個範圍內的素數(質數)的辦法有兩種:
(1)篩選法
(2)判定法,即定義法
如:求1—100間的所有素數。
分析:
用篩選法,先把2—100的數存到一個數組中,然後先把2的所有倍數刪除掉(即讓此數變為0),再刪3的倍數,繼續往上就是5的倍數,7的倍數……,最後,剩下的數(即陣列中不為0的數)就是素數。
用判定法,對2-100內的每個數,根據素數的定義,除本身外沒有其它約數來判定是否為素數.這個有個注意的地方是:找約數的時候不用找到n,也不用找到n/2,只要到sqrt(n)就行了.[別問為什麼?自己想去咯.] 具體是<?, 這裡有int 和double 的比較,有寫sqrt(n)+1,也看到有人寫sqrt(n+1) 的,哪個是正解自己多思考呵.
驗證:
以下是篩選法程式碼:
long
start = time(NULL);
long count =0;
long len = end +1;
int i,j,k;
if(end<2) return0;
//生成篩選集int* p =newint[len];
if(p == NULL) return0;
for(i=0; i<len; ++i){
p[i] = i;
}
//篩選演算法 k =2; //作為篩選的除數
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)
{
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<1) return0;
register int i;
while(beg<end+1)
{
for(i=2;i<sqrt(beg)+1; ++i){
if(beg % i ==0) break;
}
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<2) return0;
//生成篩選集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;
}
以上的演算法沒有實質改進,不過其所用的記憶體少了一半,在進行比較等操作時迴圈也只有原來一半,所用的總時間和上面的篩選演算法比也只是一半多一點,我覺得最重要的是,申請的記憶體少了,函式失敗可能性變少,而且這個改進是比較有必要的,若任由它作無謂的計算,我會很心痛咯.呵呵.
注:雖然因判定法只用了幾個變數,我想宣告為暫存器變數,可是你知道的,"儘可能"並不是"一定",何況即使得到了,還不夠快.硬體和軟體演算法的改善都有效果,硬體要成本,演算法要技術。怎麼辦?掠拌.