O(log n)でキー減少機能を実現できることは知っていますが、方法がわかりません。
「decrease-key」を効果的に実装するには、「この要素をデクリメントし、ヒープ状態が復元されるまでこの要素を子と交換する」機能にアクセスする必要があります。 heapq.py では、これは_siftdown
(および同様にインクリメントの場合は_siftup
)と呼ばれます。つまり、良いニュースは関数が存在することです...悪いニュースは、名前がアンダースコアで始まり、「内部実装の詳細」と見なされ、アプリケーションコードから直接アクセスしてはならないことを示しています(次のリリースの標準ライブラリは、そのような「内部」を使用して状況を変更し、コードを壊す可能性があります)。
警告の先頭を無視するかどうかはあなた次第です-_
、O(log N)siftingの代わりにO(N) heapify
を使用し、または、heapqの機能の一部またはすべてを再実装して、ふるい分けプリミティブを「インターフェイスのパブリック部分として公開」します。heapqのデータ構造は文書化されて公開されているため(リストのみ)、おそらく部分的な再実装が最善の選択だと思います。基本的に、ふるい分け関数をheapq.pyからアプリケーションコードにコピーします。
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()
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.
はかなり新しいので、バグでいっぱいかもしれません。
ヒープを優先キューとして使用していると想像してください。ここでは、文字列で表される一連のタスクがあり、各タスクにはキーがあります。具体的には、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 を実装しています。