web-dev-qa-db-fra.com

Comment implémenter des files d'attente prioritaires en Python?

Désolé pour une question aussi stupide mais Python sont déroutants ...

Lien 1: mise en œuvre de la file d'attente http://docs.python.org/library/queue.html

Il indique que la file d'attente a une structure pour la file d'attente prioritaire. Mais je n'ai pas trouvé comment l'appliquer.

class Queue.PriorityQueue(maxsize=0)

Lien 2: implémentation du tas http://docs.python.org/library/heapq.html

Ici, ils disent que nous pouvons implémenter indirectement des files d'attente prioritaires en utilisant heapq

pq = []                         # list of entries arranged in a heap
entry_Finder = {}               # mapping of tasks to entries
REMOVED = '<removed-task>'      # placeholder for a removed task
counter = itertools.count()     # unique sequence count

def add_task(task, priority=0):
    'Add a new task or update the priority of an existing task'
    if task in entry_Finder:
        remove_task(task)
    count = next(counter)
    entry = [priority, count, task]
    entry_Finder[task] = entry
    heappush(pq, entry)

def remove_task(task):
    'Mark an existing task as REMOVED.  Raise KeyError if not found.'
    entry = entry_Finder.pop(task)
    entry[-1] = REMOVED

def pop_task():
    'Remove and return the lowest priority task. Raise KeyError if empty.'
    while pq:
        priority, count, task = heappop(pq)
        if task is not REMOVED:
            del entry_Finder[task]
            return task
    raise KeyError('pop from an empty priority queue'

Quelle est l'implémentation de file d'attente prioritaire la plus efficace en python? Et comment le mettre en œuvre?

23
codersofthedark

La version du module Queue est implémentée à l'aide du tas , ils ont donc une efficacité égale pour les opérations de tas sous-jacentes.

Cela dit, la version Queue est plus lente car elle ajoute des verrous, l'encapsulation et une API orientée objet Nice.

Les suggestions de file d'attente prioritaire présentées dans les documents heapq sont destinées à montrer comment ajouter des fonctionnalités supplémentaires à une file d'attente prioritaire (telles que la stabilité du tri et la possibilité de modifier la priorité d'une tâche précédemment mise en file d'attente). Si vous n'avez pas besoin de ces capacités, alors le heappush de base et heappop vous donnera les performances les plus rapides.

28

Il n'y a rien de tel qu'une "implémentation de file d'attente prioritaire la plus efficace" dans n'importe quelle langue .

Une file d'attente prioritaire est tout au sujet des compromis. Voir http://en.wikipedia.org/wiki/Priority_queue

Vous devez choisir l'un de ces deux, en fonction de la façon dont vous prévoyez de l'utiliser:

  • O(log(N)) temps d'insertion et O(1) findMin + deleteMin temps, ou
  • O(1) temps d'insertion et O(log(N)) findMin + deleteMin temps

Dans ce dernier cas, vous pouvez choisir d'implémenter une file d'attente prioritaire avec un tas Fibonacci: http://en.wikipedia.org/wiki/Heap_ (data_structure) #Comparison_of_theoretic_bounds_for_variants (comme vous pouvez le voir, heapq qui est essentiellement un arbre binaire, doit nécessairement avoir O(log(N)) pour l'insertion et findMin + deleteMin)

Si vous traitez des données avec des propriétés spéciales (telles que des données délimitées), vous pouvez réaliser l'insertion de O(1) et O(1) findMin + deleteMin time. Vous ne pouvez le faire qu'avec certains types de données, sinon vous pourriez abuser de votre file d'attente prioritaire pour violer la limite O(N log(N)) lors du tri.

Pour implémenter n'importe quelle file d'attente dans n'importe quelle langue, il vous suffit de définir les opérations insert(value) et extractMin() -> value. Cela implique généralement juste un habillage minimal du tas sous-jacent; voir http://en.wikipedia.org/wiki/Fibonacci_heap pour implémenter le vôtre, ou utiliser une bibliothèque standard d'un tas similaire comme un tas d'association (une recherche Google a révélé - http://svn.python.org/projects/sandbox/trunk/collections/pairing_heap.py )


Si vous ne vous souciez que des deux que vous avez référencés sont les plus efficaces (le code basé sur heapq de http://docs.python.org/library/heapq.html#priority-queue- notes d'implémentation que vous avez inclus ci-dessus, contre Queue.PriorityQueue), puis:

Il ne semble pas y avoir de discussion facile à trouver sur le Web quant à ce que Queue.PriorityQueue est en train de faire; vous devriez plonger dans le code source, qui est lié à partir de la documentation d'aide: http://hg.python.org/cpython/file/2.7/Lib/Queue.py

   224     def _put(self, item, heappush=heapq.heappush):
   225         heappush(self.queue, item)
   226 
   227     def _get(self, heappop=heapq.heappop):
   228         return heappop(self.queue)

Comme on peut le voir, Queue.PriorityQueue utilise également heapq comme mécanisme sous-jacent. Par conséquent, ils sont également mauvais (asymptotiquement parlant). Queue.PriorityQueue peut permettre des requêtes parallèles, donc je parierais qu'il pourrait y avoir un facteur de surcharge très légèrement constant. Mais comme vous savez que l'implémentation sous-jacente (et le comportement asymptotique) doivent être les mêmes, la manière la plus simple serait simplement de les exécuter sur le même grand ensemble de données.

(Notez que Queue.PriorityQueue ne semble pas avoir de moyen de supprimer les entrées, contrairement à heapq. Cependant, il s'agit d'une arme à double tranchant: de bonnes implémentations de files d'attente prioritaires peuvent éventuellement vous permettre de supprimer des éléments dans O(1) ou O(log(N)) = heure, mais si vous utilisez le remove_task fonction que vous mentionnez, et laissez ces tâches zombies s'accumuler dans votre file d'attente parce que vous ne les extrayez pas du min, vous verrez alors un ralentissement asymptotique que vous ne verriez pas autrement. Bien sûr, vous ne pouvez pas faire cela avec Queue.PriorityQueue en premier lieu, donc aucune comparaison ne peut être faite ici.)

29
ninjagecko

Bien que cette question ait été répondue et marquée comme acceptée, voici encore une implémentation personnalisée simple de Priority Queue sans utiliser de module pour comprendre comment cela fonctionne.

# class for Node with data and priority
class Node:

  def __init__(self, info, priority):
    self.info = info
    self.priority = priority

# class for Priority queue 
class PriorityQueue:

  def __init__(self):
    self.queue = list()
    # if you want you can set a maximum size for the queue

  def insert(self, node):
    # if queue is empty
    if self.size() == 0:
      # add the new node
      self.queue.append(node)
    else:
      # traverse the queue to find the right place for new node
      for x in range(0, self.size()):
        # if the priority of new node is greater
        if node.priority >= self.queue[x].priority:
          # if we have traversed the complete queue
          if x == (self.size()-1):
            # add new node at the end
            self.queue.insert(x+1, node)
          else:
            continue
        else:
          self.queue.insert(x, node)
          return True

  def delete(self):
    # remove the first node from the queue
    return self.queue.pop(0)

  def show(self):
    for x in self.queue:
      print str(x.info)+" - "+str(x.priority)

  def size(self):
    return len(self.queue)

Trouvez le code complet et l'explication ici: https://www.studytonight.com/code/python/ds/priority-queue-in-python.php

1
iamabhishek