Computer >> Computer tutorials >  >> Programming >> Python

Program to implement Least Frequently Used Cache in Python


Suppose we want to implement a data structure for Least Frequently Used (LFU) cache system. It should support the following operations:

  • get(key) − This helps to get the value of the key if the key exists in the cache, otherwise return −1.

  • set(key, value) − This will be used to set or insert the value if the key is not already present.

When the cache reached its maximum capacity, it should invalidate the least frequently used element before inserting a new element.

So, if the LFUCache is initialized with capacity 2 and call these methods cache.set(1, 1); cache.set(2, 2); cache.get(1); cache.set(3, 3); cache.get(2); cache.set(4, 4); cache.get(1); cache.get(3); cache.get(4); then the output will be 1,−1,1,−1,4 respectively.

To solve this, we will follow these steps −

  • The initializer will take the capacity value

  • remain := capacity

  • least_freq := 1

  • node_for_freq := a map to hold the data according to insert orders

  • node_for_key := a new map

  • Define a function _update() . This will take key, value

  • x, freq := node_for_key[key]

  • delete element from node_for_freq[freq] corresponding to key

  • if size of node_for_freq[least_freq] is same as 0, then

    • least_freq := least_freq + 1

  • node_for_freq[freq+1, key] := value, freq+1

  • node_for_key[key] := value, freq+1

  • Define a function get() . This will take key

  • if key not in node_for_key is non−zero, then

    • return −1

  • value := node_for_key[key, 0]

  • _update(key, value)

  • return value

  • Define a function set() . This will take key, value

  • if key in node_for_key is non−zero, then

    • _update(key, value)

  • otherwise,

    • node_for_key[key] := value,1

    • node_for_freq[1, key] := value,1

    • if remain is same as 0, then

      • removed := delete one element from node_for_freq[least_freq] in fifo order

      • delete element from node_for_key corresponds to key removed[0]

    • otherwise,

      • remain := remain − 1

  • least_freq := 1

Let us see the following implementation to get better understanding −

Example

from collections import defaultdict, OrderedDict
class LFUCache:
   def __init__(self, capacity):
      self.remain = capacity
      self.least_freq = 1
      self.node_for_freq = defaultdict(OrderedDict)
      self.node_for_key = dict()
   def _update(self, key, value):
      _, freq = self.node_for_key[key]
      self.node_for_freq[freq].pop(key)
      if len(self.node_for_freq[self.least_freq]) == 0:
         self.least_freq += 1
      self.node_for_freq[freq+1][key] = (value, freq+1)
      self.node_for_key[key] = (value, freq+1)
   def get(self, key):
      if key not in self.node_for_key:
         return −1
      value = self.node_for_key[key][0]
      self._update(key, value)
      return value
   def set(self, key, value):
      if key in self.node_for_key:
         self._update(key, value)
      else:
         self.node_for_key[key] = (value,1)
         self.node_for_freq[1][key] = (value,1)
         if self.remain == 0:
            removed =
self.node_for_freq[self.least_freq].popitem(last=False)
            self.node_for_key.pop(removed[0])
         else:
            self.remain −= 1
         self.least_freq = 1
cache = LFUCache(2)
cache.set(1, 1)
cache.set(2, 2)
print(cache.get(1))
cache.set(3, 3)
print(cache.get(2))
cache.set(4, 4)
print(cache.get(1))
print(cache.get(3))
print(cache.get(4))

Input

cache.set(1, 1)
cache.set(2, 2)
cache.get(1)
cache.set(3, 3)
cache.get(2)
cache.set(4, 4)
cache.get(1)
cache.get(3)
cache.get(4)

Output

1
−1
1
−1
4