1. 程式人生 > 其它 >LRU演算法--python實現

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]))