web-dev-qa-db-fra.com

Comment fonctionne l'algorithme de tri de fusion externe?

J'essaie de comprendre le fonctionnement de l'algorithme de tri par fusion externe (j'ai trouvé des réponses à la même question, mais je n'ai pas trouvé ce dont j'avais besoin). Je lis le livre "Analysis Of Algorithms" de Jeffrey McConnell et j'essaie d'implémenter l'algorithme décrit ici.

Par exemple, j'ai des données d'entrée: 3,5,1,2,4,6,9,8,7 et je ne peux charger que 4 nombres en mémoire.

Ma première étape est de lire le fichier d’entrée en morceaux de 4 nombres, de les trier en mémoire et d’en écrire un dans le fichier A et à côté du fichier B.

J'ai eu: 

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

Maintenant, ma question: comment puis-je fusionner des morceaux de ces fichiers avec les plus gros s'ils ne tiennent pas dans la mémoire? Jeffrey McConnell a écrit que je devais lire des demi-morceaux et les fusionner avec les fichiers suivants C et D.

Mais je me suis trompé de séquence:

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

Quelqu'un peut-il donner un exemple avec des instructions étape par étape, s'il vous plaît?

PS: Je comprends comment fusionner numéro par numéro en lisant un fichier, mais comment puis-je le faire avec des mémoires tampons en mémoire pour réduire les opérations d’E/S?

29
KolKir

Je suppose qu'après une aussi longue période, vous devez avoir eu une réponse. Mais je fournis toujours des exemples de liens pour aider quelqu'un qui pose cette question.

REMARQUE: Avant d'examiner ce lien, vous devriez avoir une idée de la structure de données Heap Consultez Exemple de tri bidirectionnel et Exemple de tri externe multi-entrées et vous aurez une idée complète de l'implémentation d'un algorithme de tri externe

35
anuj pradhan

Tout d’abord, en triant les nombres par parties de 4, vous devriez obtenir 3 morceaux.

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

Ensuite, vous lirez la moitié de chaque fichier (ignorez C car il ne rentre pas) et les fusionnerez. Donc, vous allez charger en mémoire {[1, 2], [4, 6]}. Vous ferez une fusion occasionnelle et écrivez le résultat dans un nouveau bloc D:

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

Maintenant, la partie de A qui se trouvait dans RAM a fini de fusionner. Vous devez donc en garder la seconde moitié en mémoire. Maintenant, votre mémoire aura {[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]

Tout le morceau A a été fusionné, alors maintenant, ajoutez simplement le reste de B à D

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

Maintenant, vous devriez suivre le même processus avec les morceaux C et D. N'oubliez pas que C pourrait avoir plus d'un nombre dans un autre exemple. En fusionnant C et D, vous obtiendrez un nouveau bloc E qui sera le fichier final trié.

Notez également que, dans un exemple plus grand, vous aurez peut-être besoin de plusieurs phases de fusion. Par exemple, si vous aviez 20 chiffres à trier, vous créeriez 5 morceaux de 4 chiffres, puis vous combineriez et fusionneriez deux d'entre eux à chaque fois, pour obtenir 2 morceaux de 8 chiffres (plus un extra de 4 chiffres) puis fusionnez les nouveaux morceaux en un des 16 nombres, etc.

26
Savvas

Vous parcourerez les fichiers en même temps.

Commencez simplement par le début de chaque fichier et continuez à choisir l'élément de ce fichier qui n'est pas plus grand (c'est-à-dire plus petit ou égal), exportez cet élément dans le nouveau fichier et augmentez l'itérateur.

Dans votre dernière déclaration, il est difficile de savoir si vous savez déjà le faire ou non, mais c’est tout ce que vous devez faire, car:

  • Il vous suffirait d'avoir un numéro en mémoire pour chacun des fichiers et, bien entendu, tous les index et autres variables qui sont supposés être ignorés aux fins de cet exercice.

  • Il vous suffit de lire chaque fichier une fois, car vous pouvez garder les fichiers ouverts à la position correcte pendant ce processus. Vous n'avez donc pas besoin de relire le fichier en entier pour accéder à la position correcte.

Donc pour:

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

Vous commenceriez avec le premier élément de chaque fichier - 1 et 4.

Le 1 étant plus petit, vous le stockez dans le nouveau fichier et passez au 2.

2 est inférieur à 4, vous devez donc l'exporter et passer à 3.

3 est inférieur à 4, vous devez donc l'exporter et passer à 5.

4 est inférieur à 5, vous devez donc l'exporter et passer à 6.

5 est plus petit que 6, vous devez donc l'afficher et vous avez atteint la fin de A.

Maintenant, sortez simplement le reste de B: 6, 8, 9.

Cela vous donne [1,2,3,4,5,6,8,9].

5
Dukeling

Le tri externe est généralement utilisé lorsque vous devez trier des fichiers trop volumineux pour tenir dans la mémoire.

L'astuce consiste à diviser le fichier d'entrée plus volumineux en k morceaux plus petits triés, puis à les fusionner en un fichier trié plus volumineux. Pour la fusion, utilisez un minimum. k dépendra de votre seuil de mémoire.

Lisez un certain nombre d'enregistrements (en fonction de votre seuil de mémoire) à partir de chaque morceau et mettez-les dans une file d'attente par morceau.

Pop l'élément le plus à gauche (il s'agira du plus petit élément car les éléments de la file seront triés) de chaque file et le placer dans le tas

Pop l'élément min du tas. Notez la file d'attente

Réorganiser la file d'attente avec l'élément suivant de son bloc correspondant qui ne se trouve pas dans la file d'attente

Pop l'élément le plus à gauche de la file d'attente et le pousser au tas

Écrire l'élément min dans le fichier de sortie

Continuez les 4 étapes ci-dessus jusqu'à ce que le tas soit vide

Exemple de code python (ne fusionne pas en place)

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

Veuillez lire le fichier README pour bien comprendre le tri par fusion externe.

Il y a une implémentation pas à pas définie

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

0
user2623720