web-dev-qa-db-ja.com

Pythonの一般的な優先度キュー

Pythonコードで優先度キューを使用する必要があります。効率的なものを探して、私は heapq に出会いました。整数:比較演算子を持つすべてのオブジェクトで動作すると思いますが、必要な比較演算子は指定しません。

それに、heapqはPythonで実装されているようで、高速ではありません。

Python?で優先キューの高速実装を知っていますか?.

前もって感謝します

更新:

heapqでの再比較、(priority, object)チャーリー・マーティンが示唆するように、または単に実装する__cmp__私のオブジェクト。

heapqよりも速いものを探しています。

43
Eli Bendersky

Queue.PriorityQueue を使用できます。

Pythonは強く型付けされていないので、好きなものを保存することができます。(priority, thing)のTupleを作成するだけで設定できます。

38
Charlie Martin

最終的に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)
17
Eli Bendersky

優先度キューを使用する場合、減少キーは多くのアルゴリズム(Dijkstraのアルゴリズム、A *、OPTICS)に必須の操作です。Pythonの組み込み優先度キューがそれをサポートしていないのはなぜですか。他の回答では、この機能をサポートするソリューションを提供していません。

キーの減少操作もサポートする優先度キューは、 this Daniel Stutzbachによる実装で、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
10
Guy s

非整数要素(タプル)に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
8
Adrian

使用していませんが、 PyHeap を試すことができます。それはCで書かれているので、うまくいけばあなたにとって十分に高速です。

Heapq/PriorityQueueが十分に高速ではないことを確信していますか?そのうちの1つを使用して開始し、それが実際にパフォーマンスの問題であるかどうかをプロファイリングする価値があるかもしれません。

7
Kiv

Heapqページの "ソースの表示"リンク を見ましたか?優先キューとして(int、char)タプルのリストを持つヒープを使用する方法の半分以下の例があります。

6
Harper Shelby

これは効率的で、文字列または任意のタイプの入力に対しても機能します-:)

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

2
codersofthedark

https://pypi.python.org/pypi/fibonacci-heap-mod に優先キュー/フィボナッチヒープがあります

高速ではありません(delete-minの大きな定数c、O(c * logn))。ただし、find-min、insert、decrease-key、mergeはすべてO(1)-IOW、怠け者です。

CPythonで遅すぎる場合は、Pypy、Nuitka、またはCPython + Numbaを試してください:)

1
user1277476

Charlie Martinが示唆するように(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を呼び出すとエラーが発生します(そうすべきです!)。

0
timgeb