微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何在 Python 自定义字典中实现整数类型键的一致散列?

如何解决如何在 Python 自定义字典中实现整数类型键的一致散列?

我正在尝试用 Python 从头开始​​构建字典。我已经完成了大部分工作,但遇到了一个小问题。首先,我将首先说我使用内置的 Python hash() 来获取键的 hash_result(键可以是 int 或 str),然后索引由字典的 hash_result % 容量形成。如果键是一串字符,则一切正常。只要键是整数,我的自定义字典就会中断。有时一切正常,其他时候,键在添加到字典时获得散列值 0(例如),但在字典中搜索键时,相同的键返回散列值 4(例如),因为键返回 KeyError映射在索引 0 而不是 4。我相信,首先,索引是通过 hash(key) % capacity (4 for example) 计算的,但是一旦容量增加 x2,函数返回的索引hash(key) % capacity(现在是 8 因为 x2)是不同的,这导致了问题。我在维基百科中看到了这个公式(hash(key) % capacity)。我有兴趣了解这是否是我面临的问题,或者如果不是,究竟是什么导致了这种不受欢迎的行为以及如何解决它。 这是我的代码如下:

    class MyDictionary:
    
    __LOAD_FACTOR_LIMIT = 0.75
    __DEFAULT_CAPACITY = 4
    
    def __init__(self):
        self.__capacity = self.__DEFAULT_CAPACITY
        self.__keys = [[] for i in range(self.__capacity)]
        self.__values = [[] for i in range(self.__capacity)]

    @property
    def keys(self):
        return [item for current_list in self.__keys for item in current_list]

    @property
    def values(self):
        return [value for value_list in self.__values for value in value_list]

    def __setitem__(self,key,value):
        while self.__compute_load_factor() >= self.__LOAD_FACTOR_LIMIT:
            self.__extend_dict()

        index_hash = self.__hash_function(key)

        if self.__is_key_in_dict(index_hash,key):
            self.__set_value_to_an_existing_key(index_hash,value)
            return

        self.__set_value_to_a_new_key(index_hash,value)

    def __getitem__(self,key):
        index_hash = self.__hash_function(key)
        if self.__is_key_in_dict(index_hash,key):
            index_bucket = self.__get_index_bucket(index_hash,key)
            return self.__values[index_hash][index_bucket]
        raise KeyError('Key is not in dictionary!')

    def __str__(self):
        key_values = zip(self.keys,self.values)
        result = '{' + ",".join([f"{key}: {value}"
                                  if isinstance(key,int) else f"'{key}': {value}"
                                  for key,value in key_values]) + '}'
        return result

    def __hash_function(self,key):
        index_hash = hash(key) % self.__capacity
        return index_hash

    def __is_key_in_dict(self,index_hash,key):
        if key in self.__keys[index_hash]:
            return True
        return False

    def __get_index_bucket(self,key):
        index_bucket = self.__keys[index_hash].index(key)
        return index_bucket

    def __extend_dict(self):
        self.__keys += [[] for i in range(self.__capacity)]
        self.__values += [[] for i in range(self.__capacity)]
        self.__capacity *= 2

    def __set_value_to_an_existing_key(self,value):
        index_bucket = self.__get_index_bucket(index_hash,key)
        self.__values[index_hash][index_bucket] = value

    def __set_value_to_a_new_key(self,value):
        self.__keys[index_hash].append(key)
        self.__values[index_hash].append(value)

    def __compute_load_factor(self):
        k = len(self.__keys)
        n = len([bucket for bucket in self.__keys if bucket])
        return n / k

    def get(self,return_value=None):
        try:
            index_hash = self.__hash_function(key)
            index_bucket = self.__get_index_bucket(index_hash,key)
            if self.__is_key_in_dict(index_hash,key):
                return self.__keys[index_hash][index_bucket]
            raise KeyError('Key is not in dictionary!')
        except KeyError:
            return return_value

    def add(self):
        pass

    def pop(self):
        pass

    def clear(self):
        self.__capacity = self.__DEFAULT_CAPACITY
        self.__keys = [[] for i in range(self.__capacity)]
        self.__values = [[] for i in range(self.__capacity)]

    def items(self):
        zipped_key_value = zip(self.keys,self.values)
        return [item for item in zipped_key_value]



dictionary = MyDictionary()
dictionary.add()
dictionary[4] = 'hey'
dictionary['2'] = 'cya'
dictionary['4'] = 'welcome'
dictionary['5'] = 'welcome'
dictionary['32'] = 'heya'
dictionary['31'] = 'heya'
dictionary['36'] = 'heya'
dictionary['34'] = 'heya'
print(dictionary[4])

解决方法

这是因为当负载超过阈值时,您通过调用 __capacity 方法来增加容量(存储在 __extend_dict 属性中),这使得现有值所在的桶的索引存储不再有效,因为您总是通过对容量取模来导出索引。

因此,每次增加 dict 的容量时,您都应该在新索引处重新插入现有的键和值:

def __extend_dict(self):
    self.__capacity *= 2
    new_keys = [[] for _ in range(self.__capacity)]
    new_values = [[] for _ in range(self.__capacity)]
    for keys,values in zip(self.__keys,self.__values):
        for key,value in zip(keys,values):
            index_hash = self.__hash_function(key)
            new_keys[index_hash].append(key)
            new_values[index_hash].append(value)
    self.__keys = new_keys
    self.__values = new_values

演示:https://replit.com/@blhsing/NewEnchantingPerimeter

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。