題目:
40億 大整數,組成了一個大文件。
想找到其中的 上中位數該怎么辦?
內存:10MB,怎么辦?
內存:20K,怎么辦?
內存:有限幾個字符,怎么辦?
條件:按行讀取文件,讀取操作不占用內存。
應該具備的能力:
2^k = ? 應該都能夠熟記,達到反射性反應的程度。
字節數 對應計算機中的 容量(T, G, M, K)
內存只有 10MB 的情況
接下來我們來解題:
看到大數據容量限制的,首先想到的是從范圍入手。
1. 數據是 有符號? / 無符號?
2. 我們知道一個 4字節的無符號整數 范圍為:0~42億
那么我們可以用一個 unsigned int 來表示一個數出現的次數
(一個數最多出現 40億 次,故能夠表示,不會溢出)
3. 我們來計算 10MB 內存可以存幾個 unsigned int 的數。 => 250萬
因此我們知道 10MB 內存足夠我們在 250W 范圍內進行 精細的詞頻統計(每個數出現幾次)
4. 0~42億 內總共有 1680個 250W 范圍的段。因此我們將 42億 的范圍按照 250W 進行分段。([0, 250w), [250w,500w)...)
建立一個 1680 的數組,遍歷大文件一次,來統計每個段中出現數的個數。
count[i] 就表示在第 i 段的 連續的250W 的范圍內,出現了多少個數。
比如:一個數字值為10億,那么它應該就在 count[400] 這個段中,那么進行操作 count[400]++ 即可。
5. 對 count 進行累加,直到 sum >= 20億。這樣我們就能確定第 20億 個數是來自哪個范圍的。
6. 釋放該數組,再次遍歷大文件,利用 10MB 的空間對該范圍內的數進行 精細的詞頻統計。這樣便能夠找到中位數了。
由此可見,以上做法是借用了 桶排序 的思想。
總結:
1. 利用限制的范圍計算出我們能夠在多大的范圍內進行 精細的詞頻統計。
2. 利用 精細的范圍 對整體范圍進行划分,建立一個粗略的統計數組。
3. 遍歷大文件,找出中位數粗略的范圍。
4. 對該粗略的范圍進行精細的統計。
內存只有 20K 的情況:
我們依然用上面的方法來分析:
20K 的內存可以支持 5000范圍大小 的精細詞頻統計。
然后我們用 40億 / 5000 => 發現該數值已經怨願你大於 5000 了,我們根本無法進行粗略范圍的統計。
於是,我們不妨逆過來思考。
直接從 粗略的范圍 出發進行統計。將其分成 5000 份。
我們依然能夠得知 中位數 是在哪個部分。然后看該范圍能否被精細統計。
若不能一直循環下去。
內存只有 有限幾個字符 的情況(比如就8個字節,兩個變量):
采用二分的方法。最多二分 32 次,即需要讀 32 次文件。
1. 首先對 0~42 億二分(注意是對整個范圍進行二分),用一個變量 k 記下當前的值是多少。
遍歷文件一邊遍,計算當前小於當前值得數據個數 n。
2. 若 n < 21億,該數在小半部分。若 n > 21億,該數在大部分。
以此循環下去。