代码之家  ›  专栏  ›  技术社区  ›  Eli Bendersky

python的通用优先级队列

  •  40
  • Eli Bendersky  · 技术社区  · 16 年前

    我需要在Python代码中使用优先级队列。我四处寻找有效率的东西,突然发现 heapq . 它看起来不错,但似乎只为整数指定。我想它适用于任何具有比较运算符的对象,但它没有指定需要什么比较运算符。

    此外, heapq 似乎是用Python实现的,所以速度不快。

    您知道在python中有任何优先级队列的快速实现吗?最理想的情况是,我希望队列是通用的(即,对于具有指定比较运算符的任何对象都可以很好地工作)。

    提前谢谢

    更新:

    重新比较 HEAPQ ,我可以使用 (priority, object) 就像查理·马丁建议的那样,或者只是执行 __cmp__ 为了我的目的。

    我还在找比 HEAPQ .

    9 回复  |  直到 16 年前
        1
  •  34
  •   Charlie Martin    16 年前

    嗯, Queue.PriorityQueue ?回想一下,python不是强类型的,所以您可以保存任何您喜欢的东西:只需创建一个(priority,thing)元组就可以了。

        2
  •  17
  •   jsmedmar    9 年前

    我最终实现了 heapq ,添加dict以保持队列元素的唯一性。结果对于所有操作员都应该是相当有效的:

    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)
    
        3
  •  8
  •   Billal Begueradj    7 年前

    当使用优先级队列时,reduce key对于许多算法(dijkstra的算法,a*,optics)来说是必须的操作,我想知道为什么python的内置优先级队列不支持它。其他答案都不提供支持此功能的解决方案。

    还支持reduce key操作的优先级队列是 this DanielStutzbach的实现对于我使用Python3.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
    
        4
  •  7
  •   Kiv    16 年前

    我没用过,但你可以试试 PyHeap . 它是用C写的,所以希望它足够快。

    你肯定HeapQ/PriorityQueue不够快吗?从一开始,也许值得和他们一起分析,看看这是否真的是你的性能瓶颈。

        5
  •  6
  •   Harper Shelby damiankolasa    16 年前

    你看了吗 "Show Source" link 在HeapQ页面上?有一个例子,在使用一个具有(int,char)元组列表的堆作为优先级队列的过程中,还不到一半。

        6
  •  6
  •   Adrian    13 年前

    可以对非整数元素(元组)使用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
  •  2
  •   codersofthedark    13 年前

    这是有效的,适用于字符串或任何类型的输入-:)

    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

        8
  •  1
  •   user1277476    10 年前

    我有一个优先队列/fibonacci堆 https://pypi.python.org/pypi/fibonacci-heap-mod

    不是很快(删除min时的大常数c,即o(c*logn))。但是find-min、insert、decrease和merge都是o(1)-low,这很懒惰。

    如果对cpython的速度太慢,你可以尝试pypy、nuitka甚至cpython+numba:。

        9
  •  0
  •   timgeb    9 年前

    我可以使用 (priority, object) 就像查理·马丁建议的那样,或者只是执行 __cmp__ 为了我的目的。

    如果您希望插入的对象按特定的规则进行优先级排序,我发现编写一个简单的 PriorityQueue 它接受一个键函数。您不必插入 (优先级,对象) 手动打褶,操作更自然。

    所需行为的演示 :

    >>> 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 威尔(应该!)如果试图插入键函数无法处理的对象,则会引发错误。