Open In App

Cuckoo Hashing in Python

Last Updated : 13 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Cuckoo Hashing derived its name from the cuckoo bird, which lays its eggs in the nests of other birds, replacing their eggs with its own. Cuckoo Hashing works in a similar manner which involves moving the values to different location whenever there is a collision in the hash table. In this article, we will learn how to implement Cuckoo Hashing in Python

Cuckoo hashing maintains two separate hash tables and When a key collides in one table, it is moved to the other table using a different hash function. If the other table is also full or if the process encounters a cycle, the table is resized and rehashed.

Example to Understand Cuckoo Hashing

Suppose we have two hash tables, each with 10 slots, and two hash functions:

  • Hash function 1: h1(x) = x % 10
  • Hash function 2: h2(x) = (x // 10) % 10

Insert the keys [5, 15, 25] into the hash tables, the insertion will might like this:

  • Insert 5: h1(5) = 5 % 10 = 5. Place 5 in table 1, slot 5.
  • Insert 15: h1(15) = 15 % 10 = 5. Collision with 5 in table 1. Move 5 to table 2, slot 5. Place 15 in table 1, slot 5.
  • Insert 25: h1(25) = 25 % 10 = 5. Collision with 15 in table 1. Move 15 to table 2, slot 5. Collision with 5 in table 2. Move 5 to table 1, slot 2. Place 25 in table 2, slot 5.

Approach for Implementation of Cuckoo Hashing in Python:

Below is the Implementation of Cuckoo Hashing in python:

Python
# upper bound on number of elements in our set
MAXN = 11

# choices for position
ver = 2

# Auxiliary space bounded by a small multiple
# of MAXN, minimizing wastage
hashtable = [[float('inf')] * MAXN for _ in range(ver)]

# Array to store possible positions for a key
pos = [0] * ver

def init_table():
    """function to fill hash table with dummy value
    dummy value: float('inf')
    number of hashtables: ver"""
    for i in range(ver):
        for j in range(MAXN):
            hashtable[i][j] = float('inf')

def hash(function, key):
    """return hashed value for a key
    function: ID of hash function according to which key has to hashed
    key: item to be hashed"""
    if function == 1:
        return key % MAXN
    elif function == 2:
        return (key // MAXN) % MAXN

def place(key, table_id, cnt, n):
    """function to place a key in one of its possible positions
    table_id: table in which key has to be placed, also equal to function
    according to which key must be hashed
    cnt: number of times function has already been called in order to place
    the first input key
    n: maximum number of times function can be recursively called before
    stopping and declaring presence of cycle"""
    # if function has been recursively called max number of times, stop
    # and declare cycle. Rehash.
    if cnt == n:
        print(f"{key} unpositioned")
        print("Cycle present. REHASH.")
        return

    # calculate and store possible positions for the key. check if key
    # already present at any of the positions. If YES, return.
    for i in range(ver):
        pos[i] = hash(i + 1, key)
        if hashtable[i][pos[i]] == key:
            return

    # check if another key is already present at the position for the
    # new key in the table
    # If YES: place the new key in its position and place the older key
    # in an alternate position for it in the next table
    if hashtable[table_id][pos[table_id]] != float('inf'):
        dis = hashtable[table_id][pos[table_id]]
        hashtable[table_id][pos[table_id]] = key
        place(dis, (table_id + 1) % ver, cnt + 1, n)
    else: # else: place the new key in its position
        hashtable[table_id][pos[table_id]] = key

def print_table():
    """function to print hash table contents"""
    print("Final hash tables:")
    for i in range(ver):
        print()
        for j in range(MAXN):
            if hashtable[i][j] == float('inf'):
                print("- ", end="")
            else:
                print(f"{hashtable[i][j]} ", end="")
    print()

def cuckoo(keys, n):
    # initialize hash tables to a dummy value (float('inf'))
    # indicating empty position
    init_table()

    # start with placing every key at its position in the first
    # hash table according to first hash function
    for i in range(n):
        cnt = 0
        place(keys[i], 0, cnt, n)

    # print the final hash tables
    print_table()

# driver function
def main():
    # following array doesn't have any cycles and
    # hence all keys will be inserted without any
    # rehashing 
    keys_1 = [20, 50, 53, 75, 100, 67, 105, 3, 36, 39]

    cuckoo(keys_1, len(keys_1))

    # following array has a cycle and hence we will
    # have to rehash to position every key
    keys_2 = [20, 50, 53, 75, 100, 67, 105, 3, 36, 39, 6]

    cuckoo(keys_2, len(keys_2))

if __name__ == "__main__":
    main()

Output
Final hash tables:

- 100 - 36 - - 50 - - 75 - 
3 20 - 39 53 - 67 - - 105 - 
105 unpositioned
Cycle present. REHASH.
Final hash tables:

- 67 - 3 - - 39 - - 53 - 
6 20 - 36 50 - 75 - - 100 - 

Time Complexity: O(N)
Auxiliary Space: O(N)


Next Article
Article Tags :

Similar Reads