隨筆-3  評論-0  文章-2  trackbacks-0

          題目:在一個文件中有 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<K,且k個數可以完全讀進內存,那么先構建k個數的堆,先找出第0到k大的數,再掃描一遍數組找出第k+1到2k的數,再掃描直到找出第K個數。雖然每次時間大約是nlog(k),但需要掃描ceil(K/k) 次,這里要掃描5次。

          解法:首先假設是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的整數冪,映射起來也很方便。

           

          原文地址 http://www.cppblog.com/richbirdandy/

          posted on 2010-04-13 09:22 海大哥 閱讀(319) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 冀州市| 米易县| 崇信县| 汝南县| 嘉兴市| 景东| 盐边县| 永清县| 仁寿县| 杭锦后旗| 西宁市| 武清区| 永春县| 长乐市| 双峰县| 红河县| 乌鲁木齐县| 化州市| 景洪市| 犍为县| 荣昌县| 建阳市| 灵璧县| 武清区| 观塘区| 洪雅县| 洪泽县| 长丰县| 饶阳县| 揭阳市| 长泰县| 木里| 新干县| 秀山| 永定县| 福泉市| 凉山| 颍上县| 临沂市| 大方县| 高雄市|