歸並排序,外排序,10G文件500M內存的排序


老是被我家寶貝問這種類似的問題, 然后干脆寫一篇相關文章吧

歸並排序可以是一種外排序, 外排序是指利用外存也就是磁盤進行排序的一種簡稱。

典型的應用是hadoop 的 mapreduce 的merge 階段

歸並排序的: 假設有n 個元素, 將n 個元素分程x 組, 然后對每一組的元素進行排序, 然后將這 x 組已經排好序的序列合並起來。

說一下分成x 組的方式, 大概有兩種:

  第一種: 遞歸的方式,  這種情況你可能都不會去關心 x 大小。

  第二種: 每組 m 個元素, x =  n/m。這種方法常用在內存不夠的情況下。

 

所以歸並排序的步驟分兩步:

  第一步: 分x 組, 對每組的元素進行排序

  第二部: 合並, 將已經排序好的 x 組元素合並起來。

 

先說第一種遞歸的方式吧, 就是你可能會做到的歸並排序的題目:

  我們先寫第二步的代碼:合並已經排序好的數組

def merge_sorted_list(l1, l2):
    i, j = 0, 0
    result = []
    while i < len(l1) and j < len(l2):
        if l1[i] <= l2[j]:
            result.append(l1[i])
            i += 1
        else:
            result.append(l2[j])
            j += 1
    result += l1[i:]
    result += l2[j:]
    return result

  再說第一步:

  

def merge_sort(l):
    if len(l) <= 1:
        return l
    num = len(l) / 2
    l1 = merge_sort(l[:num])
    l2 = merge_sort(l[num:])
    return merge_sorted_list(l1, l2)

全部代碼:

def merge_sort(l):
    if len(l) <= 1:
        return l
    num = len(l) / 2 
    l1 = merge_sort(l[:num])
    l2 = merge_sort(l[num:])
    return merge_sorted_list(l1, l2) 
  
def merge_sorted_list(l1, l2):
    i, j = 0, 0
    result = []
    while i < len(l1) and j < len(l2):
        if l1[i] <= l2[j]:
            result.append(l1[i])
            i += 1
        else:
            result.append(l2[j])
            j += 1
    result += l1[i:]
    result += l2[j:]
    return result
  
a = [219, 9527, 211, 9218]
print a
b = merge_sort(a)
print b

總的來說這種歸並排序理解起來以及代碼寫起來都是很簡單的。

 

接下來說一下第二種吧,純粹的外排序。用栗子的方式理解起來比較簡單。

問題: 假設有2G的文件,里面是數字,一行一個數字,內存只有500M。讓你排序,咋整?

其實思路很簡單,一次只讀一點點,排序好,然后再將將文件合並起來。然后最終保存為一個大文件。

先說下python open(filepath).readline(),一次只會返回一行,不必擔心大文件,相信各個語言都有對應的方式。

首先第一步,大文件拆分成 x 個 block_size 大的小文件,每個小文件排好序

def split_file(file_path, block_size):
    f = open(file_path, 'r')
    fileno = 1 
    files = []
    while True:
        lines = f.readlines(block_size)
        if not lines:
            break
        lines = [int(i.strip()) for i in lines]
        lines.sort()
        files.append(save_file(lines, fileno))
        fileno += 1
    return files

第二部:將拆分成的小文件合並起來,然后將歸並的東西寫到大文件里面去, 這里用到的是多路歸並的方法。多路歸並在我的這篇文章里面有寫到:http://www.cnblogs.com/dream-of-cambridge/articles/8046031.html

def nw_merge(files):
    fs = [open(file_) for file_ in files]
    min_map = {}
    out = open("/home/hrs/demo/files/out", "a")
    for f in fs:
        read = f.readline()
        if read:
            min_map[f] = int(read.strip())
    
    while min_map:
        min_ = min(min_map.items(), key = lambda x: x[1])
        min_f, min_v = min_
        out.write("{}".format(min_v))
        out.write("\n")
        nextline = min_f.readline()
        if nextline:
            min_map[min_f] = int(nextline.strip())
        else:
            del min_map[min_f]

全部代碼:

 

import os

def save_file(l, fileno):
    filepath = "/home/hrs/demo/files/{}" .format(fileno)

    f = open(filepath, 'a')
    for i in l:
        f.write("{}".format(i))
        f.write("\n")
    f.close()
    return filepath


def nw_merge(files):
    fs = [open(file_) for file_ in files]
    min_map = {} # 用來記錄每一路當前最小值。
    out = open("/home/hrs/demo/files/out", "a")
    for f in fs:
        read = f.readline()
        if read:
            min_map[f] = int(read.strip())

    while min_map:
     # 將最小值取出, 並將該最小值所在的那一路做對應的更新 min_
= min(min_map.items(), key = lambda x: x[1]) min_f, min_v = min_ out.write("{}".format(min_v)) out.write("\n") nextline = min_f.readline() if nextline: min_map[min_f] = int(nextline.strip()) else: del min_map[min_f] def split_file(file_path, block_size): f = open(file_path, 'r') fileno = 1 files = [] while True: lines = f.readlines(block_size) if not lines: break lines = [int(i.strip()) for i in lines] lines.sort() files.append(save_file(lines, fileno)) fileno += 1 return files if __name__ == "__main__": file_path = "/home/hrs/demo/ta" block_size = 500*1024*1024 #500M num_blocks = os.stat(file_path).st_size/block_size files = split_file(file_path, block_size) nw_merge(files)

 

  


免責聲明!

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



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