題目:在乙個檔案中有 10g 個整數,亂序排列,要求找出中位數。記憶體限制為 2g。只寫出思路即可(記憶體限制為 2g的意思就是,可以使用2g的空間來執行程式,而不考慮這台機器上的其他軟體的占用記憶體)。
關於中位數:資料排序後,位置在最中間的數值。即將資料分成兩部分,一部分大於該數值,一部分小於該數值。中位數的位置:當樣本數為奇數時,中位數=(n+1)/2 ; 當樣本數為偶數時,中位數為n/2與1+n/2的均值(那麼10g個數的中位數,就第5g大的數與第5g+1大的數的均值了)。
分析:明顯是一道工程性很強的題目,和一般的查詢中位數的題目有幾點不同。
1. 原資料不能讀進記憶體,不然可以用快速選擇,如果數的範圍合適的話還可以考慮桶排序或者計數排序,但這裡假設是32位整數,仍有4g種取值,需要乙個16g大小的陣列來計數。
2. 若看成從n個數中找出第k大的數,如果k個數可以讀進記憶體,可以利用最小或最大堆,但這裡k=n/2,有5g個數,仍然不能讀進記憶體。
3. 接上,對於n個數和k個數都不能一次讀進記憶體的情況,《程式設計之美》裡給出乙個方案:設k
解法:首先假設是32位無符號整數。
1. 讀一遍10g個整數,把整數對映到256m個區段中,用乙個64位無符號整數給每個相應區段記數。
說明:整數範圍是0 - 2^32 - 1,一共有4g種取值,對映到256m個區段,則每個區段有16(4g/256m = 16)種值,每16個值算一段, 0~15是第1段,16~31是第2段,……2^32-16 ~2^32-1是第256m段。乙個64位無符號整數最大值是0~8g-1,這裡先不考慮溢位的情況。總共占用記憶體256m×8b=2gb。
2. 從前到後對每一段的計數累加,當累加的和超過5g時停止,找出這個區段(即累加停止時達到的區段,也是中位數所在的區段)的數值範圍,設為[a,a+15],同時記錄累加到前乙個區段的總數,設為m。然後,釋放除這個區段占用的記憶體。
3. 再讀一遍10g個整數,把在[a,a+15]內的每個值計數,即有16個計數。
4. 對新的計數依次累加,每次的和設為n,當m+n的值超過5g時停止,此時的這個計數所對應的數就是中位數。
總結:1.以上方法只要讀兩遍整數,對每個整數也只是常數時間的操作,總體來說是線性時間。
2. 考慮其他情況。
若是有符號的整數,只需改變對映即可。若是64為整數,則增加每個區段的範圍,那麼在第二次讀數時,要考慮更多的計數。若過某個計數溢位,那麼可認定所在的區段或代表整數為所求,這裡只需做好相應的處理。噢,忘了還要找第5g+1大的數了,相信有了以上的成果,找到這個數也不難了吧。
3. 時空權衡。
花費256個區段也許只是恰好配合2gb的記憶體(其實也不是,呵呵)。可以增大區段範圍,減少區段數目,節省一些記憶體,雖然增加第二部分的對單個數值的計數,但第一部分對每個區段的計數加快了(總體改變??待測)。
4. 對映時盡量用位操作,由於每個區段的起點都是2的整數冪,對映起來也很方便。
產生N個隨機數,使用選擇排序,對隨機數進行排序
include include include define n 100 資料個數 define u 1000 資料個數 int data n 存放資料的陣列 int comp count 0 資料比較次數 int swap count 0 資料交換次數 新增隨機數到陣列 void add data...
如何產生乙個隨機數
大家可能很多次討論過隨機數在計算機中怎樣產生的問題,在這篇文章中,我會對這個問題進行更深入的 闡述我對這個問題的理解。首先需要宣告的是,計算機不會產生絕對隨機的隨機數,計算機只能產生 偽隨機數 其實絕對隨機的隨機數只是一種理想的隨機數,即使計算機怎樣發展,它也不會產生一串絕對隨機的隨機數。計算機只能...
產生N個隨機數,使用插入排序,對隨機數進行排序
include include include define n 100 資料個數 define u 1000 資料範圍 int data n 存放資料的陣列 int comp count 0 資料比較次數 int swap count 0 資料交換次數 新增隨機數到陣列 void add data...