2012-07-10 25 views
7

私は純粋なSTLを使ってLFU(Least Frequently Used)キャッシュを実装しようとしています(私はBoostを使いたくありません!)。STLを使用してLFUキャッシュを実装する方法は?

要件は以下のとおりです。std::mapと同じようKeyを使って任意の要素に

  • 連想アクセス。
  • 最低優先度のアイテムを解放する機能(UsesCount属性を使用)。
  • アイテムの優先度(UsesCount)を更新する機能。問題がある

  • Iアイテム(KeyValueUsesCount)のコンテナとしてstd::vectorを使用する場合、連想アクセスとstd::make_heap用ベクターへのイテレータの容器としてstd::mapstd::push_heapstd::pop_heapベクトル内での優先度キューの実装として、マップ内のイテレータはヒープ操作後に有効ではありません。
  • 前のコンフィグレーションでstd::vectorの代わりにstd::list(またはstd::map)を使用すると、イテレータがaritmeticをサポートしないため、std::make_heapなどをコンパイルできません。
  • std::priority_queueを使用したい場合、アイテムの優先度を更新する機能はありません。

質問は以下のとおりです。

  • 私はこの問題を解決する方法を明らかに何かが足りないのですか?
  • 以前の要件を満たすLFUキャッシュの純粋なC++/STL実装を例として挙げてください。

あなたの洞察をいただきありがとうございます。

+3

「マップでitertorsがヒープ・オペレーションの後に有効ではありません」 - それを他の方法で回避を行うことによってこの問題を解決 - 他の要素が挿入されている場合でも、それが移動し得ることはありません 'map'にデータを置きます/消去されました。次に、マップイテレータをベクトルに置き、そこからヒープを構築します。しかし、アイテムの優先度を効率的に更新する機能がまだ不足している可能性があるので、これは答えではありません。 –

+0

私の頭を越えていない別のアイデアをありがとう。しかし、もし 'std :: vector'イテレータの' std :: vector'を持っていれば、どのように 'UsesCount'属性のpointeeオブジェクトの中にある比較演算子を定義すれば' std :: make_heap'を実行するか、 'UsesCount'を更新しますか? – Blackhex

+3

ような比較ファンクタを使用して: 'ブール演算子()(MapIter、MapIterB){> second.UseCount < b-> second.UseCount A-リターン。 } ' – Useless

答えて

2

*_heap関数を使用してmakeを実装すると、ベクターがうまくフィットしているようです。更新が遅くなることもあります。発生するイテレータの無効化に関する問題は、基本的なデータ構造としてベクトルを使用するすべてのコンテナで通常です。これはまたboost::heap::priority_queueによって取られたアプローチですが、上記の理由で可変インターフェースを提供しません。 boost::heap data-structuresは、ヒープを更新する機能を提供します。

少し奇妙なもの:std::priority_queueを使用することができますが、イテレータの無効化の問題に直面します。

あなたの質問に直接答えるには:あなたは何かが明らかに欠けているわけではありません。 std::priority_queueはそれほど有用ではありません。最良の方法は、更新をサポートする独自のヒープ実装を作成することです。完全にSTL互換(特にアロケータ対応)にするのはやや難しく、単純な作業ではありません。さらに、LFUキャッシュを実装します。

最初のステップは、Boostの実装を見て、その努力のアイデアを得ることです。私は2番目のリファレンス実装について認識していません。それは追加コストを作成し、非常に厄介な得ることができるよう、あなたがそれを回避しようとすべきであるが、あなたは常に、別の容器に間接を選択することができますイテレータの無効化を回避するために

+0

優先度を '* _heap'でどのように更新しますか?私はそれがここで仕事をすることができない 'priority_queue'だけではないと思う:標準的なヒープ関数はできません。したがって、質問者は別のヒープ実装が必要です。しかし、私は間違っている可能性があります。 –

+1

@SteveJessop私はここで間違っているかもしれませんが、 'make_heap'を呼び出す優先順位の更新の後にヒープを修正する必要があります。しかしこれはおそらく最適ではありません。 – pmr

+0

合意。それはヒープ不変を復元しますが、それは 'O(n)'です。他のヒープ実装は 'O(log n)'で増減/更新できます。 –

1

A 2つのデータ構造を維持するよりも、やや単純なアプローチ:

  • ちょうど彼らの価値/使用カウントペアにあなたのキーをマッピングマップをキープ。
  • キャッシュがいっぱいです:
    O(n))最悪の10%を見つけるためにstd::nth_elementを使用
    • O(n))地図要素にイテレータのベクトルを作成します
      • マップからそれらのすべてを削除する(O(n log n)

    ので、キャッシュに新しい要素を追加する一般的なケースO(log n)、最悪の場合O(n log n)で、O(log n)を償却。新しいエントリがトップの90%を作るために持っているか、それらはカットしているので、最悪の10%を削除

    は、LFUキャッシュにビット抜本的なかもしれません。また、1つの要素だけを削除しても、新しいエントリは、次の新しいエントリの前に一番下から降りる必要があります。だからなぜLFUがあなたにとって正しいキャッシング戦略なのかによって、私の変更は間違った戦略かもしれないし、それでも問題ないかもしれません。

  • +0

    ハッシュマップを使用して、これらのオペレーションの多くでO(1)を取得できます。 – Puppy

    +0

    @DeadMG:良い点、質問者がSTLを指定しているので、間違いなく利用可能です。 C++にはありません03(BoostまたはTR1なし) –

    関連する問題