2012-07-10 164 views
7

我正在嘗試使用純STL實現LFU(Least Frequently Used)緩存(我不想使用Boost!)。如何使用STL實現LFU緩存?

要求是:

  • 到任何元件關聯的訪問使用Keystd::map
  • 能夠釋放最低優先級項目(使用其UsesCount屬性)。
  • 能夠更新任何項目的優先級(UsesCount)。

的問題是:

  • 如果我使用std::vector作爲std::map項的容器(KeyValueUsesCount),作爲迭代器的用於關聯存取和std::make_heapstd::push_heapstd::pop_heap該載體的容器作爲向量中的優先級隊列實現,映射中的迭代器在堆操作後無效。
  • 如果我在前面的配置中使用std::list(或std::map)而不是std::vector,則std::make_heap等無法編譯,因爲它們的迭代器不支持aritmetic。
  • 如果我想使用std::priority_queue,我無法更新項目優先級。

的問題是:

  • 我失去了一些東西很明顯這個問題如何解決?
  • 你能指點我一些滿足以前需求的LFU緩存的純C++/STL實現嗎?

謝謝你的見解。

+3

「堆映射中的迭代器在堆操作後無效」 - 通過其他方式解決此問題 - 將數據放入映射中,即使插入了其他元素也不會移動/擦除。然後把map迭代器放到你的向量中,並從中構建一個堆。儘管如此,你可能仍然缺少有效更新項目優先級的能力,所以這不是一個答案。 – 2012-07-10 08:47:17

+0

謝謝你的另一個想法,我不介意。但是如果我有'std :: map'迭代器的'std :: vector',我怎麼能夠定義它們的比較運算符,它會在'UsesCount'屬性中查看指針對象的內部,以便能夠使用'項目插入後或「UsesCount」更新後的std :: make_heap'? – Blackhex 2012-07-10 08:52:55

+3

使用比較函數,如:bool operator()(MapIter a,MapIterB){return a-> second.UseCount < b-> second.UseCount; }' – Useless 2012-07-10 09:10:50

回答

2

使用*_heap函數和向量來製作實現似乎非常合適。儘管它會導致更新緩慢。對於使用向量作爲底層數據結構的每個容器,遇到的有關迭代器失效的問題都是正常的。這也是boost::heap::priority_queue採用的方法,但由於上述原因,它不提供可變接口。其他boost::heap data-structures提供更新堆的能力。

一些看起來有點奇怪的事情:即使你能夠使用std::priority_queue,你仍然會面臨迭代器失效問題。

直接回答你的問題:你不會錯過一些明顯的東西。 std::priority_queue不如它應該是有用的。最好的方法是編寫自己的支持更新的堆實現。爲了完全兼容STL(尤其是分配器感知)是相當棘手的,而不是一個簡單的任務。最重要的是,實現LFU緩存。

第一步,查看Boost實現以瞭解該工作。我不知道第二個參考實現。

要解決迭代器失效問題,您可以隨時選擇間接到另一個容器中,儘管您應該儘量避免它,因爲它會產生額外的成本,並且可能會變得非常混亂。

+0

如何用'* _heap'更新優先級?我認爲這不僅僅是'priority_queue',不能完成這項工作:標準堆函數不能。所以提問者需要一個不同的堆實現。雖然我可能是錯的。 – 2012-07-10 09:16:50

+1

@SteveJessop也許我在這裏錯了,但在更新一次優先級調用'make_heap'後應該修復堆。不過,這可能遠不是最優的。 – pmr 2012-07-10 09:22:24

+0

同意。這將恢復堆不變,但它是'O(n)'。其他堆實現可以在'O(log n)'中增加/減少/更新。 – 2012-07-10 09:23:23

1

一個稍微簡單不是保持兩個數據結構的方法:

  • 只是不停地在地圖上,您的鍵映射到自己的價值/使用計數對。
  • 當緩存滿:
    • 創建迭代的向量到映射的元素(O(n)
    • 使用std::nth_element找到最差的10%(O(n)
    • 將它們全部從地圖中刪除(O(n log n)

因此,增加了高速緩存中的新元素是常見的情況O(log n),最壞的情況下O(n log n)和攤銷O(log n)

在LFU緩存中去除最糟糕的10%可能有點激烈,因爲新條目必須達到最高90%,或者它們被削減。再次,如果你只刪除一個元素,那麼新的條目仍然需要在下一個新條目之前脫離底部,否則它們被切斷,並且他們沒有時間這樣做。所以,根據LFU爲什麼適合您的緩存策略,我對它的改變可能是錯誤的策略,或者它可能還是很好的。

+0

你可以用散列圖得到O(1)對於許多這些操作。 – Puppy 2012-07-10 18:19:36

+0

@DeadMG:好點,提問者指定STL,所以肯定有一個可用。沒有C++ 03(沒有Boost或TR1) – 2012-07-10 18:54:09