web-dev-qa-db-ja.com

Pythonで優先度キューを実装する方法は?

このような馬鹿げた質問は申し訳ありませんが、Python docs is confusing .。

リンク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で最も効率的な優先キューの実装はどれですか?そして、それを実装する方法は?

23
codersofthedark

Queueモジュールのバージョンは 実装済みheapqモジュールを使用しているため、基礎となるヒープ操作。

ただし、Queueバージョンは、ロック、カプセル化、およびNiceオブジェクト指向APIを追加するため、低速です。

heapqドキュメントに示されている優先度キューの提案 は、優先度キューに追加機能を追加する方法(並べ替えの安定性、以前にキューに入れられたタスクの優先度を変更する機能など)を示すためのものです。これらの機能が必要ない場合は、基本的なheappushおよびheappop関数が最速のパフォーマンスを提供します。

28

任意の言語に「最も効率的な優先度キュー実装」などはありません。

優先度キューは、トレードオフに関するものです。 http://en.wikipedia.org/wiki/Priority_queue を参照してください

使用方法に基づいて、次の2つのいずれかを選択する必要があります。

  • O(log(N))挿入時間およびO(1) findMin + deleteMin時間、または
  • O(1)挿入時間およびO(log(N)) findMin + deleteMin時間

後者の場合、フィボナッチヒープで優先度キューを実装することを選択できます。 http://en.wikipedia.org/wiki/Heap_(data_structure)#Comparison_of_theoretic_bounds_for_variants 基本的にバイナリツリーであるheapqは、挿入とfindMin + deleteMinの両方に必ずO(log(N))が必要です。

特別なプロパティ(制限されたデータなど)を持つデータを扱う場合、O(1)挿入とO(1) findMin + deleteMin時間を達成できます。特定の種類のデータでのみこれを行うことができます。そうしないと、優先キューを乱用して、ソートのO(N log(N))バインドに違反する可能性があります。

任意の言語で任意のキューを実装するには、insert(value)およびextractMin() -> value操作を定義するだけです。通常、これには、基礎となるヒープの最小限のラップが含まれます。 http://en.wikipedia.org/wiki/Fibonacci_heap を参照して独自の実装を行うか、ペアリングヒープ(Googleのような類似のヒープの既製ライブラリを使用してください検索結果 http://svn.python.org/projects/sandbox/trunk/collections/pairing_heap.py


参照した2つのうちどちらがより効率的かだけを気にする場合( http://docs.python.org/library/heapq.html#priority-queue-implementation-notesのheapqベースのコード 上に含めたもの、対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)) time、ただし、言及したremove_task関数を使用し、それらのゾンビタスクをキューに蓄積する場合それらを最小値から抽出していないので、他の方法では見られない漸近的なスローダウンが表示されます。もちろん、最初にQueue.PriorityQueueを使用してこれを行うことはできません。ここで作った。)

29
ninjagecko

この質問は回答済みで、承認済みとマークされていますが、モジュールを使用せずに動作を理解するためのPriority Queueの簡単なカスタム実装がここにあります。

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

完全なコードと説明はこちらでご覧ください: https://www.studytonight.com/code/python/ds/priority-queue-in-python.php

1
iamabhishek