Подтвердить что ты не робот

Как реализовать приоритетные очереди в Python?

Извините за такой глупый вопрос, но документы Python запутывают...

Ссылка 1: Реализация очереди http://docs.python.org/library/queue.html

В нем говорится, что Queue имеет структуру для очереди приоритетов. Но я не мог найти, как его реализовать.

class Queue.PriorityQueue(maxsize=0)

Ссылка 2: реализация кучи http://docs.python.org/library/heapq.html

Здесь они говорят, что мы можем реализовать приоритетные очереди косвенно, используя 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'

Какая наиболее эффективная реализация очереди приоритетов в python? И как его реализовать?

4b9b3361

Ответ 1

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

Тем не менее, версия очереди медленнее, поскольку добавляет блокировки, инкапсуляцию и хороший объектно-ориентированный API.

Предложения приоритетной очереди, показанные в документах heapq, предназначены для того, чтобы показать, как добавить дополнительные возможности в приоритетную очередь (например, стабильность сортировки и возможность изменения приоритет ранее заданной задачи). Если вам не нужны эти возможности, то основные функции heappush и heappop предоставят вам максимальную производительность.

Ответ 2

В любом языке нет такой вещи, как "наиболее эффективная реализация очереди приоритетов".

Очередь приоритетов - это все о компромиссах. См. http://en.wikipedia.org/wiki/Priority_queue

Вы должны выбрать один из этих двух, исходя из того, как вы планируете его использовать:

  • O(log(N)) время вставки и O(1) findMin + deleteMin time, или
  • O(1) время вставки и O(log(N)) findMin + deleteMin time

В последнем случае вы можете выбрать очередь приоритетов с кучей Fibonacci: http://en.wikipedia.org/wiki/Heap_(data_structure)#Comparison_of_theoretic_bounds_for_variants (как видите, heapq, который является в основном двоичным деревом, обязательно должен иметь O(log(N)) для вставки и findMin + deleteMin)

Если вы имеете дело со данными со специальными свойствами (такими как ограниченные данные), вы можете достичь вставки O(1) и O(1) findMin + deleteMin time. Вы можете делать это только с определенными типами данных, потому что иначе вы могли бы злоупотреблять своей очередью приоритетов, чтобы нарушить привязку O(N log(N)) при сортировке.

Чтобы реализовать любую очередь на любом языке, вам нужно всего лишь определить операции insert(value) и extractMin() -> value. Обычно это связано с минимальной упаковкой основной кучи; см. http://en.wikipedia.org/wiki/Fibonacci_heap, чтобы реализовать свою собственную или использовать встроенную библиотеку подобной кучи, такой как Pairing Heap (обнаружен поиск в Google http://svn.python.org/projects/sandbox/trunk/collections/pairing_heap.py)


Если вам все равно, какой из двух ссылок вы более эффективны (код heapq от http://docs.python.org/library/heapq.html#priority-queue-implementation-notes, который вы включенный выше, в сравнении с Queue.PriorityQueue), затем:

Кажется, что в Интернете нет легкодоступного обсуждения того, что на самом деле делает Queue.PriorityQueue; вам нужно было бы начать погружение в код, связанный с справочной документацией: 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)

Как мы видим, Queue.PriorityQueue также использует heapq в качестве основного механизма. Поэтому они одинаково плохи (асимптотически). Queue.PriorityQueue может допускать параллельные запросы, поэтому я бы сделал ставку на то, что он может иметь слишком незначительный накладные расходы. Но поскольку вы знаете, что основная реализация (и асимптотическое поведение) должна быть одинаковой, самым простым способом было бы просто запустить их на одном и том же большом наборе данных.

(Обратите внимание, что Queue.PriorityQueue, похоже, не имеет способа удалить записи, тогда как heapq делает. Однако это обоюдоострый меч: реализация приоритетов с хорошим приоритетом может позволить вам удалить элементы в O ( 1) или O (log (N)), но если вы используете функцию remove_task, которую вы упомянули, и пусть эти задачи зомби накапливаются в вашей очереди, потому что вы не извлекаете их с минимума, то вы увидите асимптотическое замедление которые вы в противном случае не видели бы. Конечно, вы не могли бы сделать это с помощью Queue.PriorityQueue в первую очередь, поэтому здесь не может быть никакого сравнения.)