web-dev-qa-db-ja.com

Pythonのヒープにキーを減らす機能を実装するにはどうすればよいですか?

O(log n)でキー減少機能を実現できることは知っていますが、方法がわかりません。

36
zerodx

「decrease-key」を効果的に実装するには、「この要素をデクリメントし、ヒープ状態が復元されるまでこの要素を子と交換する」機能にアクセスする必要があります。 heapq.py では、これは_siftdown(および同様にインクリメントの場合は_siftup)と呼ばれます。つまり、良いニュースは関数が存在することです...悪いニュースは、名前がアンダースコアで始まり、「内部実装の詳細」と見なされ、アプリケーションコードから直接アクセスしてはならないことを示しています(次のリリースの標準ライブラリは、そのような「内部」を使用して状況を変更し、コードを壊す可能性があります)。

警告の先頭を無視するかどうかはあなた次第です-_、O(log N)siftingの代わりにO(N) heapifyを使用し、または、heapqの機能の一部またはすべてを再実装して、ふるい分けプリミティブを「インターフェイスのパブリック部分として公開」します。heapqのデータ構造は文書化されて公開されているため(リストのみ)、おそらく部分的な再実装が最善の選択だと思います。基本的に、ふるい分け関数をheapq.pyからアプリケーションコードにコピーします。

36
Alex Martelli

Decrease-keyは、多くのアルゴリズム(DijkstraのAlgorithm、A *、OPTICS)で必須の操作ですが、Pythonの組み込みの優先度キューがサポートしていないのはなぜですか。

残念ながら、math4totsのパッケージをダウンロードできませんでした。

しかし、私はDaniel Stutzbachによる this 実装を見つけることができました。 Python 3.5で完璧に動作しました。

hd = heapdict()
hd[obj1] = priority
hd[obj1] = lower_priority
# ...
obj = hd.pop()
8
Guy s

heapqドキュメント には、これを行う方法に関するエントリがあります。

ただし、これを正確に実行するheapパッケージを作成しました(これはheapqのラッパーです)。したがって、pipまたはeasy_installがある場合は、次のようなことができます。

pip install heap

次に、コードに書き込みます

from heap.heap import heap

h = heap()

h['hello'] = 4 # Insert item with priority 4.

h['hello'] = 2 # Update priority/decrease-key has same syntax as insert. 

かなり新しいので、バグでいっぱいかもしれません。

5
math4tots

ヒープを優先キューとして使用していると想像してください。ここでは、文字列で表される一連のタスクがあり、各タスクにはキーがあります。具体的には、task_list = [[7,"do laundry"], [3, "clean room"], [6, "call parents"]]を参照してください。ここで、task_listの各タスクは、優先度と説明が記載されたリストです。 heapq.heapify(task_list)を実行すると、配列がヒープ不変を維持できるようになります。ただし、「洗濯を行う」の優先度を1に変更する場合は、ヒープを線形スキャンしない限り、「洗濯を行う」がヒープ内のどこにあるかを知る方法はありません(したがって、対数時間でdecrease_keyを実行することはできません)。 。注decrease_key(heap, i, new_key)では、ヒープ内で変更する値のインデックスを知っている必要があります。

各サブリストへの参照を維持し、実際にキーを変更しても、ログ時間でそれを行うことはできません。リストは一連の可変オブジェクトへの単なる参照であるため、タスクの元の順序を覚えておくようなことを試してみてください(この場合、「洗濯をする」は元のtask_listの0番目のタスクでした):

task_list = [[7, "do laundry"], [3, "clean room"], [6, "call parents"]]
task_list_heap = task_list[:] # make a non-deep copy
heapq.heapify(task_list_heap)
# at this point:
# task_list = [[7, 'do laundry'], [3, 'clean room'], [6, 'call parents']]
# task_list_heap = [3, 'clean room'], [7, 'do laundry'], [6, 'call parents']]
# Change key of first item of task_list (which was "do laundry") from 7 to 1.
task_list[0][0] = 1
# Now:
# task_list = [[1, 'do laundry'], [3, 'clean room'], [6, 'call parents']]
# task_list_heap = [3, 'clean room'], [1, 'do laundry'], [6, 'call parents']]
# task_list_heap violates heap invariant at the moment

ただし、ログ時間でヒープ不変を維持するには、heapq._siftdown(task_list_heap, 1)を呼び出す必要があります(heapq.heapifyは線形時間です)が、残念ながら、task_list_heap(この場合はheap_index)の「洗濯を行う」のインデックスはわかりません。ケースは1)です。

したがって、各オブジェクトのheap_indexを追跡するヒープを実装する必要があります。たとえば、list(ヒープ用)とdictを使用して、各オブジェクトをヒープ/リスト内のインデックスにマッピングします(ヒープ位置がスワップされると更新され、それぞれに定数係数が追加されますスワップ)。 heapq.py を読み通して、手順が簡単なので自分で実装できます。ただし、他の人はすでにこの種の HeapDict を実装しています。

5
dr jimbob