it-swarm.com.ru

Как работает алгоритм внешней сортировки слиянием?

Я пытаюсь понять, как работает внешний алгоритм сортировки слиянием (я нашел ответы на один и тот же вопрос, но не нашел того, что мне нужно). Я читаю книгу «Анализ алгоритмов» Джеффри Макконнелла и пытаюсь реализовать алгоритм, описанный там.

Например, у меня есть входные данные: 3,5,1,2,4,6,9,8,7, и я могу загрузить только 4 числа в память.

Мой первый шаг - прочитать входной файл в виде 4-значных блоков, отсортировать их в памяти и записать один в файл A, а затем в файл B.

Я получил: 

A:[1,2,3,5][7]  
B:[4,6,8,9]

Теперь мой вопрос, как я могу объединить куски из этих файлов с большими, если они не помещаются в память? Джеффри Макконнелл написал, что мне нужно прочитать половину фрагментов и объединить их в следующие файлы C и D.

Но я получил неправильную последовательность:

C:[1,2,4,6,3,8,5,9]
D:[7]

Кто-нибудь может привести пример с пошаговой инструкцией, пожалуйста?

PS: я понимаю, как объединять число за номером, читая из файла, но как мне сделать это с буферами в памяти, чтобы уменьшить количество операций ввода-вывода?

29
KolKir

Я думаю, что после такого долгого времени у тебя, должно быть, есть ответ. Но я все еще предоставляю некоторые примеры ссылок, чтобы помочь кому-то еще, кто задал этот вопрос.

ПРИМЕЧАНИЕ. Перед просмотром этой ссылки вы должны иметь представление о структуре данных Heap Взгляните на Пример двусторонней сортировки и Пример многосторонней внешней сортировки и вы получите полное представление о реализации алгоритма внешней сортировки

35
anuj pradhan

Прежде всего, сортируя числа по частям по 4 числа, вы должны получить 3 порции.

A:[1,2,3,5]  
B:[4,6,8,9]
C:[7]

Затем вы прочитаете половину каждого файла (игнорируйте C, так как он не подходит) и объедините их. Итак, вы загрузите в память {[1, 2], [4, 6]}. Вы сделаете случайное слияние и запишите результат в новый блок D:

Compare 1 and 4 -> D:[1]
Compare 2 and 4 -> D:[1, 2]

Теперь часть A, которая была в RAM, завершила слияние, так что теперь вам нужно будет перенести вторую половину в память. Теперь ваша память будет иметь {[3, 5], [4, 6]}.

Compare 3 and 4 -> D:[1, 2, 3]
Compare 5 and 4 -> D:[1, 2, 3, 4]
Compare 5 and 6 -> D:[1, 2, 3, 4, 5]

Все фрагменты A были объединены, так что теперь просто добавьте остаток B в D

D:[1,2,3,4,5,6,8,9]

Теперь вам придется выполнить тот же процесс с кусками C и D. Помните, что C может иметь более одного числа в другом примере. Объединяя C и D, вы получите новый фрагмент E, который будет последним отсортированным файлом.

Также обратите внимание, что в более крупном примере вам может понадобиться больше фаз слияния. Например, если у вас есть 20 чисел для сортировки, вы создадите 5 кусков по 4 числа, а затем вы будете объединять и объединять два из них каждый раз, что приведет к получению 2 кусков по 8 чисел (плюс один дополнительный из 4 чисел), и затем объедините новые фрагменты в одно из 16 чисел и так далее.

26
Savvas

Вы будете перебирать файлы одновременно.

Просто начните с начала каждого файла и продолжайте выбирать, какой элемент файла не больше (то есть меньше или равен) другого, выводите этот элемент в новый файл и увеличивайте итератор.

Из вашего последнего утверждения неясно, знаете ли вы это уже или нет, но это все, что вам нужно сделать, потому что:

  • Вам нужно было бы иметь только одно число в памяти для каждого из файлов, и, конечно, любые индексы и другие переменные, которые предположительно игнорируются для целей этого упражнения.

  • Вам нужно прочитать каждый файл только один раз, так как вы можете держать файлы открытыми в правильном положении во время этого процесса, поэтому вам не нужно снова читать весь файл, чтобы добраться до правильного положения.

Таким образом, для:

A:[1,2,3,5]
B:[4,6,8,9]

Вы начинаете с первого элемента из каждого файла - 1 и 4.

1 меньше, поэтому вы выводите его в новый файл и переходите к 2.

2 меньше, чем 4, поэтому вы выводите его и переходите к 3.

3 меньше, чем 4, поэтому вы выводите его и переходите к 5.

4 меньше, чем 5, поэтому вы выводите его и переходите к 6.

5 меньше, чем 6, поэтому вы выводите его, а затем вы достигли конца A.

Теперь просто выведите остаток B: 6, 8, 9.

Это дает вам [1,2,3,4,5,6,8,9].

5
Dukeling

Внешняя сортировка обычно используется, когда вам нужно отсортировать файлы, которые слишком велики, чтобы уместиться в память.

Хитрость заключается в том, чтобы разбить больший входной файл на k отсортированных меньших фрагментов, а затем объединить фрагменты в больший отсортированный файл. Для слияния используйте минимальную кучу. К будет зависеть от вашего порога памяти.

Прочитайте определенное количество записей (в зависимости от порога памяти) из каждого чанка и поместите его в очередь на чанк.

Извлеките самый левый элемент (это будет самый маленький элемент, так как элементы в очереди будут отсортированы) из каждой очереди и поместите его в кучу

Поместите минимальный предмет из кучи. Обратите внимание, из какой очереди это произошло

Пополнить очередь следующим элементом из соответствующего куска, которого нет в очереди

Выдвиньте самый левый элемент из очереди и поместите его в кучу

Запишите элемент min в выходной файл

Продолжайте описанные выше 4 шага, пока куча не опустеет

Пример кода Python (не объединяется на месте)

import os
import heapq
import itertools
import linecache
from collections import deque
import sys


def external_sort(input_directory, input_file_name, output_file_name):
    with open(os.path.expanduser(input_directory + '/' + output_file_name), 'w+') as f:
        heap = []
        pages = {}
        next_line_numbers = {}
        has_more_items = {}
        chunk_file_paths, max_chunk_size = create_sorted_chunks(input_directory, input_file_name)
        max_page_size = max_chunk_size // 10
        for chunk_file_path in chunk_file_paths:
            pages[chunk_file_path] = populate_page(chunk_file_path, max_page_size)
            next_line_numbers[chunk_file_path] = len(pages[chunk_file_path])
            has_more_items[chunk_file_path] = True
        for chunk_file_path in chunk_file_paths:
            heapq.heappush(heap, pages[chunk_file_path].popleft())
        while heap:
            item, chunk_file_path = heapq.heappop(heap)
            f.write(str(item)+'\n')
            if has_more_items[chunk_file_path]:
                has_more_items[chunk_file_path] = append_next(pages, chunk_file_path, next_line_numbers[chunk_file_path])
                next_line_numbers[chunk_file_path] += 1
            if pages[chunk_file_path]:
                heapq.heappush(heap, pages[chunk_file_path].popleft())
    for chunk_file_path in chunk_file_paths:
        os.remove(chunk_file_path)


def populate_page(chunk_file_path, max_page_size):
    chunk = deque()
    with open(chunk_file_path, 'r') as f:
        for line in itertools.islice(f, 0, max_page_size):
            chunk.append((int(line), chunk_file_path))
    return chunk


def append_next(chunks, chunk_file_path, line_number):
    chunk = chunks[chunk_file_path]
    item = linecache.getline(chunk_file_path, line_number)
    if item and len(item) > 0:
        chunk.append((int(item), chunk_file_path))
        has_more = True
    else:
        has_more = False
    return has_more


def create_sorted_chunks(input_file_directory, input_file_name):
    input_file_path = os.path.expanduser(input_file_directory + '/' + input_file_name)
    suffix = 1
    begin, end, tot = 0, 0, 0
    chunk_file_paths = []
    with open(input_file_path, 'r') as f:
        for line in f.readlines():
            tot += 1
    end = tot//10
    while suffix <= 10:
        buffer = []
        chunk_file_name = 'temp' + str(suffix) + '.txt'
        chunk_file_path = os.path.expanduser(input_file_directory + '/' + chunk_file_name)
        if not os.path.isfile(chunk_file_path):
            with open(os.path.expanduser(input_file_path), 'r') as f:
                for line in itertools.islice(f, begin, end):
                    buffer.append(int(line))
                create_chunk(chunk_file_path, buffer)
        suffix += 1
        begin = end
        end += tot//10
        chunk_file_paths.append(chunk_file_path)
    return chunk_file_paths, tot//10


def create_chunk(chunk_file_path, buffer):
    buffer.sort()
    with open(chunk_file_path, 'w+') as f:
        for i in buffer:
            f.write(str(i) + '\n')


if __== '__main__':
    external_sort(sys.argv[1], sys.argv[2], sys.argv[3])
2
lalatnayak

Пожалуйста, прочитайте файл README, чтобы правильно понять сортировку слиянием.

Определена пошаговая реализация

https://github.com/melvilgit/external-Merge-Sort/blob/master/README.md

0
user2623720