web-dev-qa-db-ja.com

カスタム比較述語を使用したheapq

カスタムソート述語でヒープを構築しようとしています。それに入る値は「ユーザー定義」タイプなので、組み込みの比較述部を変更することはできません。

次のようなことを行う方法はありますか?

h = heapq.heapify([...], key=my_lt_pred)
h = heapq.heappush(h, key=my_lt_pred)

さらに良いことに、自分のコンテナにheapq関数をラップすることができるので、述語を渡す必要がありません。

54
vsekhar

heapq documentation によれば、ヒープ順序をカスタマイズする方法は、ヒープ上の各要素をタプルにすることです。最初のタプル要素は、通常のPython比較。

Heapqモジュール内の関数は(オブジェクト指向ではないため)少し面倒で、常に最初のパラメーターとしてヒープオブジェクト(ヒープ化されたリスト)を明示的に渡す必要があります。 key関数を指定し、ヒープをオブジェクトとして提示できるようにする非常に単純なラッパークラスを作成することにより、1つの石で2羽の鳥を殺すことができます。

以下のクラスは内部リストを保持します。各要素はタプルで、その最初のメンバーはキーであり、keyパラメーターを使用して要素の挿入時に計算され、ヒープのインスタンス化で渡されます。

# -*- coding: utf-8 -*-
import heapq

class MyHeap(object):
   def __init__(self, initial=None, key=lambda x:x):
       self.key = key
       if initial:
           self._data = [(key(item), item) for item in initial]
           heapq.heapify(self._data)
       else:
           self._data = []

   def Push(self, item):
       heapq.heappush(self._data, (self.key(item), item))

   def pop(self):
       return heapq.heappop(self._data)[1]
87
jsbueno

heapq documentation は、ヒープ要素が、最初の要素が優先順位であるタプルであり、ソート順を定義できることを示唆しています。

ただし、あなたの質問に関連するのは、ドキュメントに サンプルコードとの議論 が含まれていることです。その他の問題)。

一言で言えば、その解決策は、heapqの各要素を、優先度、エントリ数、および挿入する要素を持つトリプルにすることです。エントリカウントにより、同じ優先順位を持つ要素がヒープに追加された順序でソートされることが保証されます。

10
srgerg

両方の答えの制限は、同点を同点として扱うことを許可しないことです。 1つ目は、項目を比較することでタイが壊れ、2つ目は、入力順序を比較することで壊れます。ネクタイをネクタイにするだけの方が速く、それらがたくさんある場合、大きな違いを生む可能性があります。上記およびドキュメントに基づいて、これがheapqで達成できるかどうかは明らかではありません。 heapqがキーを受け入れないのに対して、同じモジュール内のキーから派生した関数が受け入れるのは奇妙に思えます。

1
bbphd