【轉】海量數據求中位數


 

以下是轉載的:個人感覺第四種方法很巧妙,O(N)復雜度最好也最快!第二種方法不太理解~

騰訊一面問到了,用的算法導論中的Kth算法,期望時間復雜度為O(n)。后來想了想,萬一數據多的來根本不能一次讀入內存,這個時候該如何解決呢?

題目如下:
只有2G內存的pc機,在一個存有10G個整數的文件,從中找到中位數,寫一個算法。

給出了四種方法來解決

算法:

1.利用外排序的方法,進行排序 ,然后再去找中位數
注釋:外部排序基本上由兩個相對獨立的階段組成。首先,按可用內存大小,將外存上含n個記錄的文件分成若干長度為h的子文件,依次讀入內存並利用有效的內部排序方法對它們進行排序,並將排序后得到的有序子文件重新寫入外存,通常稱這些有序子文件為歸並段或順串;然后,對這些歸並段進行逐趟歸並,使歸並段(有序的子文件)逐漸由小到大,直至得到整個有序文件為止。
 
2.另外還有個思路利用堆
先求第1G大,然后利用該元素求第2G大,然后利用第2G大,求第3G大...當然這樣的話雖不需排序,但是磁盤操作會比較多,具體還需要分析下與外排序的效率哪個的磁盤IO會比較多
建立一個1g個整數的最大值堆,如果元素小於最大值則入堆,這樣可以得到第1g大的那個元素然后利用這個元素,重新建一次堆,這次入堆的條件還要加上大於這個第1g大的元素,這樣建完堆可以得到第2g大的那個...
 
3.借鑒基數排序思想
偶認為可以用位來判斷計數,從最高位到最低位,為了方便表述我們假設為無符號整數,即0x00000000~0xFFFFFFFF依次遞增,那么可以遍歷所有數據,並記錄最高位為0和1的個數(最高位為0的肯定是小於最高位為1的)記為N0、N1
那么根據N0和N1的大小就可以知道中位數的最高位是0還是1
假設N0>N1,那么再計算N00和N01,
如果N00>(N01+N1),則說明中位數的最高兩位是00
再計算N000和N001.。。。依次計算就能找到中位數
 
如果改進一下,設定多個計數器
好像一次磁盤io也可以統計出N0,N00,....的數值
 
4.借鑒桶排序思想
一個整數假設是32位無符號數
第一次掃描把0~2^32-1分成2^16個區間,記錄每個區間的整數數目
找出中位數具體所在區間65536*i~65536*(i+1)-1
第二次掃描則可找出具體中位數數值


第一次掃描已經找出中位數具體所在區間65536*i~65536*(i+1)-1

然后第二次掃描再統計在該區間內每個數出現的次數,就可以了

題目:在一個文件中有 10G 個整數,亂序排列,要求找出中位數。內存限制為 2G。只寫出思路即可(內存限制為 2G的意思就是,可以使用2G的空間來運行程序,而不考慮這台機器上的其他軟件的占用內存)。
分析:既然要找中位數,很簡單就是排序的想法。那么基於字節的桶排序是一個可行的方法
思想:將整形的每1byte作為一個關鍵字,也就是說一個整形可以拆成4個keys,而且最高位的keys越大,整數越大。如果高位keys相同,則比較次高位的keys。整個比較過程類似於字符串的字典序。
第一步:把10G整數每2G讀入一次內存,然后一次遍歷這536,870,912個數據。每個數據用位運算">>"取出最高8位(31-24)。這8bits(0-255)最多表示255個桶,那么可以根據8bit的值來確定丟入第幾個桶。最后把每個桶寫入一個磁盤文件中,同時在內存中統計每個桶內數據的數量,自然這個數量只需要255個整形空間即可。
代價:(1) 10G數據依次讀入內存的IO代價(這個是無法避免的,CPU不能直接在磁盤上運算)。(2)在內存中遍歷536,870,912個數據,這是一個O(n)的線性時間復雜度。(3)把255個桶寫會到255個磁盤文件空間中,這個代價是額外的,也就是多付出一倍的10G數據轉移的時間。
第二步:根據內存中255個桶內的數量,計算中位數在第幾個桶中。很顯然,2,684,354,560個數中位數是第1,342,177,280個。假設前127個桶的數據量相加,發現少於1,342,177,280,把第128個桶數據量加上,大於1,342,177,280。說明,中位數必在磁盤的第128個桶中。而且在這個桶的第1,342,177,280-N(0-127)個數位上。N(0-127)表示前127個桶的數據量之和。然后把第128個文件中的整數讀入內存。(平均而言,每個文件的大小估計在10G/128=80M左右,當然也不一定,但是超過2G的可能性很小)。
代價:(1)循環計算255個桶中的數據量累加,需要O(M)的代價,其中m<255。(2)讀入一個大概80M左右文件大小的IO代價。
注意,變態的情況下,這個需要讀入的第128號文件仍然大於2G,那么整個讀入仍然可以按照第一步分批來進行讀取。
第三步:繼續以內存中的整數的次高8bit進行桶排序(23-16)。過程和第一步相同,也是255個桶。
第四步:一直下去,直到最低字節(7-0bit)的桶排序結束。我相信這個時候完全可以在內存中使用一次快排就可以了。
整個過程的時間復雜度在O(n)的線性級別上(沒有任何循環嵌套)。但主要時間消耗在第一步的第二次內存-磁盤數據交換上,即10G數據分255個文件寫回磁盤上。一般而言,如果第二步過后,內存可以容納下存在中位數的某一個文件的話,直接快排就可以了。關於快排的效率,可以看看我博客中的數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM