今天晚上的目標就是實現乙個外排序的演算法,最近幾天多多少少的看了點這方面的文章,還有一些實現,之前對這個概念十分不清晰,其實現在想來,外排序的操作檔案,其實和操作記憶體一樣,只不過它的速度實在是太慢了。但在**上幾乎沒有區別,把記憶體上的定義資料,轉變成對檔案的讀入讀出。
其實現在還在繼續研究中,打算先把完成的一部分貼出來,然後全部完成後,再拿出完整版,這樣有個思考的過程。不過說時候現在完成的這部分我都不好意思拿出來。。。還是皮厚點吧。。上**
這個函式是分割檔案的,把乙個大檔案分割成n個小檔案,從大檔案中讀取的資料量需指定,還要分割成的碎片檔案的大小,當然得到的碎片檔案是通過快排排序好的(隨機法改進的快排)。
int datacut(char* filename,int numsize,int totalsize)
quicksort(a,0,numleft-1);
for(i=0;i>a[i];
}quicksort(a,0,numsize-1);
for(i=0;i快排的**其實之前的日誌裡有,不過這個版本是修改過的,也貼出來。
int qsort_partion(int* a,int start,int end
} swap(a[end],a[i]);//將當前值於比較值對換,然後就完成以i 為界,一邊的值都小於a[i],另一邊都大於a[i]
return i;
}int rand_partion(int *a,int start,int end)//這個就是修改的部分
void quicksort(int* a, int start ,int end)
int j,k;
j=0;k=0;
while(k<1024 && j<1024)
void datainit()//生成資料
~autobuf()
void attachfile(const char* filename)
int read()//從指定檔案中讀取
size=i;//buf中有多少個資料
cout<<"read size:"<=size )
else//還有,可以繼續讀
}} return tmp;
}};
這個類其實就是乙個中間作用,減少檔案讀寫的次數,基本一次讀入可以用好久了。本來還想寫乙個 減少檔案寫入的類,發現相似點太多了,改進也挺容易,就先留著這個問題。
k路合併**,目前只能處理一次合併的,遞迴多次合併即如何自動化完成全套流程,後面再想。
void k_merge(int k,const char* targetfile)//k路歸併成一路
好吧,我承認我最後虐待了下電腦,生成了1億個數字的檔案,然後切割排序,但只歸併一百路,用了93s。。
最後檢查資料的時候發現幾個檔案間歇性的出現亂碼,想到下午那個問題,一查,果然是處理檔案流的時候忘記flush了,吃一見長一智啊。這次加上就完全沒問題了。
11.26最終測試後發現,有很多bug,比較嚴重的是,結果中居然出現-1.這個問題比較嚴重,然後就是效率偏低,直接歸併法果然複雜度很高,資料量大的情況會越發明顯。得引入其他的處理方法,比如敗者樹或者最小堆,今天就到這裡了,改日慢慢改進。
11.27日,實現使用維護k大的最小堆,來進行歸併。
補充一部分維護堆的**,還有修改k_merge函式。
struct heapnode
;typedef heapnode* point;
void adjust_heap(point* a , int i)//調節該點 ok
else
}a[i]=tmp;
}void make_heap( point* heap , int size )// ok
}void adjustdown_heap(point* a ,int size)
else
}if( tmp->knum < a[2*i]->knum && tmp->knum < a[(2*i+1)]->knum )//兩個孩子
else//選擇和較小的孩子節點替換
else}}
a[i] = tmp;
}void k_merge(int k,const char* targetfile)//k路歸併成一路
make_heap(node,size);
while( size > 0 )
else//堆大小減1,同時將其塞入廢棄區
}for(i=1;i<=k;++i) delete node[i];
out.close();
}
經測試發現這種方法比以前的快些,尤其是k的值比較大的時候,但如果k值很小,或者讀寫的檔案塊很大的時候,效率一般。相對樸素演算法只是稍有提高而已。
繼續修改,因為感覺效率實在太差了,所以做了進一步的嘗試,由於最終結果是得到乙個數字寫入乙個,所以我覺得這裡會有瓶頸,還是決定新增乙個作為中間快取的類autowriter,這個和autobuf的相當,只是負責寫入端,沒想到加入以後修改完,效果特別好。一下子把10的7次方資料的排序,從57s降到了34s,降低了23s,幾乎是一大半的時間啊,原來檔案讀寫的速度如此之慢。僅僅是塊讀取和一次一次讀就差距這麼大。
先上**,k_merge又改了。。這篇文章顯得很亂,但正顯示了一次一次的思考過程。
首先是autowriter類
const int w_buf=4096;//存滿w_buf個整數後寫入檔案
struct autowriter
~autowriter()
void attachfile(const char * filename)
void put(int num)
make_heap(node,size);
while( size > 0 )
else//堆大小減1,同時將其塞入廢棄區
}for(i=1;i<=k;++i) delete node[i];
}
現在就差最後兩個嘗試了,敗者樹可能比最小堆效率好些,然後就是能一次性的自動化完成多次k路歸併。
今天先到這裡了。。
11.29 在摸索了一段時間後,終於著手寫敗者樹實現的外部排序了。開始對敗者樹理解不到位,還對於完全二叉樹的理解也不夠,其實敗者樹說簡單點就是:一堆記錄比賽結果的節點(k)個,第0個位置記錄的是最後的勝利者,每個節點的值是該節點子節點比賽的失敗者。然後是k個選手,所以這個樹的結構其實就是ls[0-k] players[0-k],然後只要你知道某個選手的索引,如s,那麼他的父親節點(即比賽結果記錄的敗者)的索引就是(2+k)/2 。每次資料更新,只需要和父親節點記錄的索引的選手比一次 ,然後記錄敗者,勝者沿樹上公升,直到根節點,這就是一輪調整。而調整的過程使用於建樹,只要初始化乙個最小值,然後對每乙個選手節點進行調整就好,但不一定總知道所排序的數字的最小值和最大值嘛,那就隨便找乙個不衝突的作為該節點未比賽的記錄,只要遇到這個值,就預設為勝者就成了。
這部分**不貼出來了,佔空間,就放在我的**裡。
內排序和外排序
內排序 指在排序期間資料物件全部存放在記憶體的排序。外排序 指在排序期間全部物件太多,不能同時存放在記憶體中,必須根據排序過程的要求,不斷在內,外存間移動的排序。根據排序元素所在位置的不同,排序分 內排序和外排序。內排序 在排序過程中,所有元素調到記憶體中進行的排序,稱為內排序。內排序是排序的基礎。...
內排序和外排序的理解
內排序 指在排序期間資料物件全部存放在記憶體的排序。外排序 指在排序期間全部物件太多,不能同時存放在記憶體中,必須根據排序過程的要求,不斷在內,外存間移動的排序。根據排序元素所在位置的不同,排序分 內排序和外排序 內排序 在排序過程中,所有元素調到記憶體中進行的排序,稱為內排序。內排序是排序的基礎。...
20140528 歸併排序 內排序 外排序
1 歸併排序 2 內排序和外排序 外排序的乙個例子是外歸併排序 external merge sort 它讀入一些能放在記憶體內的資料量,在記憶體中排序後輸出為乙個順串 即是內部資料有序的臨時檔案 處理完所有的資料後再進行歸併。比如,要對 900 mb 的資料進行排序,但機器上只有 100 mb 的...