LFU Cache in Python

PythonServer Side ProgrammingProgramming

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

  • get(key) – This will be used to get the value of the key if the key exists in the cache, otherwise return -1.

  • put(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.put(1, 1); cache.put(2, 2); cache.get(1); cache.put(3, 3); cache.get(2); cache.put(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 put() . 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

Example 

Let us see the following implementation to get better understanding −

 Live Demo

import collections
class LFUCache:
   def __init__(self, capacity):
      self.remain = capacity
      self.least_freq = 1
      self.node_for_freq = collections.defaultdict(collections.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 put(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.put(1, 1)
cache.put(2, 2)
print(cache.get(1))
cache.put(3, 3)
print(cache.get(2))
cache.put(4, 4)
print(cache.get(1))
print(cache.get(3))
print(cache.get(4))

Input

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

Output

1
-1
1
-1
4
raja
Published on 21-Jul-2020 08:59:43
Advertisements