LRU演算法--python實現
最近最少使用演算法LRU(Least Recently Used)
原理:
按照使用時間倒排序,然後從尾部刪除元素。
場景:
-
在有限的空間中,儲存物件時,當空間滿時,會按一定的原則刪除原有的物件,
常用的原則(演算法)LRU, FIFO, LFU等。 -
計算機的Cache硬體,以及主存到虛擬記憶體的頁面置換,Redis快取系統。
LRU演算法比較簡單,當對key進行訪問時,將該key放置到佇列的最前端(或者最後端),這樣
實現了對key按最後一次訪問的時間降序(或升序)排列,當向空間增加新物件時,如果空間
滿了,刪除隊尾(或隊首)物件。
python中使用collections.OrderedDict很方便實現LRU演算法。
下面的實現是有問題的,這個cache的key:value鍵值對中,value只能是不可變型別。因為,如果value是可變型別,那對於同一個key,所有呼叫get(key)方法返回的value都是指向同一個可變物件的,當修改其中一個value時,那所有的value都會被修改了,即使你沒有呼叫set()方法也會這樣。這是我們不希望看到的。解決方法我想到了兩種,一是可變物件序列化後再儲存,即將可變物件轉為不可變物件;二是仍儲存可變物件,但get()時,返回一個深拷貝,這樣每個get()呼叫返回的物件就不會相互影響了。推薦第一種方法。另外,對於key,推薦使用str/unicode型別。
當併發時,還會存在一個問題,因為這涉及到對公共資源的寫操作,所以必須要對set()加鎖。其實,在併發情況下,所有對公共資源的寫操作都要加鎖。如果不存在併發的情況,只有單執行緒,那可以不加鎖。
""" implement the LRU(leasted Recently Used) """ # -*- coding: utf-8 -*- from collections import OrderedDict class LRUCache(OrderedDict): """ 不能儲存可變型別物件,不能併發訪問set() """ def __init__(self, capacity): self.capacity = capacity self.cache = OrderedDict() def get(self, key): if self.cache.__contains__(key): value = self.cache.pop(key) self.cache[key] = value else: value = None return value def set(self, key, value): if self.cache.__contains__(key): value = self.cache.pop(key) self.cache[key] = value else: if len(self.cache) == self.capacity: self.cache.popitem(last=False) self.cache[key] = value else: self.cache[key] = value if __name__ == '__main__': c = LRUCache(5) for i in range(5, 10): c.set(i, 10*i) print(f"{c.cache, c.cache.keys()}") c.get(5) c.get(7) print(f"{c.cache, c.cache.keys()}")
結果:
(OrderedDict([(5, 50), (6, 60), (7, 70), (8, 80), (9, 90)]), odict_keys([5, 6, 7, 8, 9]))
(OrderedDict([(6, 60), (8, 80), (9, 90), (5, 50), (7, 70)]), odict_keys([6, 8, 9, 5, 7]))