Общая приоритетная очередь для Python - PullRequest
43 голосов
/ 02 января 2009

Мне нужно использовать приоритетную очередь в моем коде Python. В поисках чего-то эффективного я наткнулся на heapq . Это выглядит хорошо, но, кажется, указано только для целых чисел. Я предполагаю, что он работает с любыми объектами, у которых есть операторы сравнения, но он не определяет, какие операторы сравнения ему нужны.

Кроме того, heapq, кажется, реализован в Python, поэтому он не быстрый.

Вам известны какие-либо быстрые реализации приоритетных очередей в Python? Оптимально, я бы хотел, чтобы очередь была универсальной (то есть хорошо работала для любого объекта с указанным оператором сравнения).

Заранее спасибо

Обновление:

Для сравнения в heapq я могу либо использовать (priority, object), как предлагает Чарли Мартин, либо просто реализовать __cmp__ для моего объекта.

Я все еще ищу что-то быстрее, чем heapq.

Ответы [ 9 ]

38 голосов
/ 02 января 2009

Вы можете использовать Queue.PriorityQueue .

Вспомните, что Python не является строго типизированным, поэтому вы можете сохранять все что угодно: просто наберите (priority, thing) и все готово.

17 голосов
/ 02 января 2009

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

class PriorityQueueSet(object):

    """
    Combined priority queue and set data structure.

    Acts like a priority queue, except that its items are guaranteed to be
    unique. Provides O(1) membership test, O(log N) insertion and O(log N)
    removal of the smallest item.

    Important: the items of this data structure must be both comparable and
    hashable (i.e. must implement __cmp__ and __hash__). This is true of
    Python's built-in objects, but you should implement those methods if you
    want to use the data structure for custom objects.
    """

    def __init__(self, items=[]):
        """
        Create a new PriorityQueueSet.

        Arguments:
            items (list): An initial item list - it can be unsorted and
                non-unique. The data structure will be created in O(N).
        """
        self.set = dict((item, True) for item in items)
        self.heap = self.set.keys()
        heapq.heapify(self.heap)

    def has_item(self, item):
        """Check if ``item`` exists in the queue."""
        return item in self.set

    def pop_smallest(self):
        """Remove and return the smallest item from the queue."""
        smallest = heapq.heappop(self.heap)
        del self.set[smallest]
        return smallest

    def add(self, item):
        """Add ``item`` to the queue if doesn't already exist."""
        if item not in self.set:
            self.set[item] = True
            heapq.heappush(self.heap, item)
9 голосов
/ 03 мая 2016

При использовании очереди с приоритетами, уменьшение ключа является обязательной операцией для многих алгоритмов (Алгоритм Дейкстры, A *, OPTICS), мне интересно, почему встроенная очередь приоритетов Python не поддерживает ее. Ни один из других ответов не предоставляет решение, которое поддерживает эту функцию.

Очередь приоритетов, которая также поддерживает операцию уменьшения клавиши, - эта реализация Даниэля Штутцбаха отлично сработала для меня с Python 3.5.

from heapdict import heapdict

hd = heapdict()
hd["two"] = 2
hd["one"] = 1
obj = hd.popitem()
print("object:",obj[0])
print("priority:",obj[1])

# object: one
# priority: 1
7 голосов
/ 12 октября 2011

Вы можете использовать heapq для нецелых элементов (кортежей)

from heapq import *

heap = []
data = [(10,"ten"), (3,"three"), (5,"five"), (7,"seven"), (9, "nine"), (2,"two")]
for item in data:
    heappush(heap, item)
sorted = []
while heap:
    sorted.append(heappop(heap))
print sorted
data.sort()
print data == sorted
7 голосов
/ 02 января 2009

Я не использовал его, но вы можете попробовать PyHeap . Это написано на C, так что, надеюсь, это достаточно быстро для вас.

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

6 голосов
/ 02 января 2009

Вы смотрели на ссылку "Показать источник" на странице heapq? Пример чуть менее половины использования кучи со списком кортежей (int, char) в качестве очереди с приоритетами.

2 голосов
/ 01 апреля 2012

Это эффективно и работает для строк или ввода любого типа -:)

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')

Справка: http://docs.python.org/library/heapq.html

1 голос
/ 06 ноября 2014

У меня приоритетная очередь / куча Фибоначчи в https://pypi.python.org/pypi/fibonacci-heap-mod

Это не быстро (большая константа c на delete-min, которая равна O (c * logn)). Но поиск мин, вставка, клавиша уменьшения и слияние - все O (1) - IOW, это лениво.

Если это слишком медленно на CPython, вы можете попробовать Pypy, Nuitka или даже CPython + Numba:)

0 голосов
/ 18 января 2016

Я могу либо использовать (priority, object), как предлагает Чарли Мартин, либо просто реализовать __cmp__ для моего объекта.

Если вы хотите, чтобы вставленные объекты были расположены по приоритетам в соответствии с определенным правилом, я нашел очень полезным написать простой подкласс PriorityQueue, который принимает функцию ключа. Вам не придется вставлять (priority, object) кортежи вручную, и обработка будет более естественной.

Демонстрация желаемого поведения :

>>> h = KeyHeap(sum)
>>> h.put([-1,1])
>>> h.put((-1,-2,-3))
>>> h.put({100})
>>> h.put([1,2,3])
>>> h.get()
(-1, -2, -3)
>>> h.get()
[-1, 1]
>>> h.get()
[1, 2, 3]
>>> h.get()
set([100])
>>> h.empty()
True
>>>
>>> k = KeyHeap(len)
>>> k.put('hello')
>>> k.put('stackoverflow')
>>> k.put('!')
>>> k.get()
'!'
>>> k.get()
'hello'
>>> k.get()
'stackoverflow'

Код Python 2

from Queue import PriorityQueue

class KeyHeap(PriorityQueue):
    def __init__(self, key, maxsize=0):            
        PriorityQueue.__init__(self, maxsize)
        self.key = key

    def put(self, x):
        PriorityQueue.put(self, (self.key(x), x))

    def get(self):
        return PriorityQueue.get(self)[1]

Код Python 3

from queue import PriorityQueue

class KeyHeap(PriorityQueue):
    def __init__(self, key, maxsize=0):            
        super().__init__(maxsize)
        self.key = key

    def put(self, x):
        super().put((self.key(x), x))

    def get(self):
        return super().get()[1]

Очевидно, что вызов put вызовет (и должен!) Вызвать ошибку, если вы попытаетесь вставить объект, который ваша ключевая функция не может обработать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...