Python的哈希函数顺序背后的逻辑是什么?

Kas*_*mvd 4 python hashtable python-2.7 python-3.x python-internals

我们知道,Python的一些数据结构使用哈希表来存储像set或的项目dictionary.所以这些对象没有顺序.但似乎对某些数字序列而言并非如此.

例如,请考虑以下示例:

>>> set([7,2,5,3,6])
set([2, 3, 5, 6, 7])

>>> set([4,5,3,0,1,2])
set([0, 1, 2, 3, 4, 5])
Run Code Online (Sandbox Code Playgroud)

但是,如果我们进行一些小改动,它就没有排序:

>>> set([8,2,5,3,6])
set([8, 2, 3, 5, 6])
Run Code Online (Sandbox Code Playgroud)

所以问题是:Python的哈希函数如何对整数序列起作用?

Kas*_*mvd 9

虽然SO中有很多问题hash及其顺序,但没有人解释哈希函数的算法.

所以你需要知道python如何计算哈希表中的索引.

如果你hashtable.c在CPython源代码中浏览文件,你会在_Py_hashtable_set函数中看到以下几行,它们显示了python计算哈希表键索引的方式:

key_hash = ht->hash_func(key);
index = key_hash & (ht->num_buckets - 1);
Run Code Online (Sandbox Code Playgroud)

因此,整数的哈希值是整数本身*(-1除外),索引基于数据结构的数量和长度(ht->num_buckets - 1),并使用Bitwise和之间(ht->num_buckets - 1)以及数字计算.

现在考虑set使用hash-table 的以下示例:

>>> set([0,1919,2000,3,45,33,333,5])
set([0, 33, 3, 5, 45, 333, 2000, 1919])
Run Code Online (Sandbox Code Playgroud)

33我们有多少:

33 & (ht->num_buckets - 1) = 1
Run Code Online (Sandbox Code Playgroud)

实际上它是:

'0b100001' & '0b111'= '0b1' # 1 the index of 33
Run Code Online (Sandbox Code Playgroud)

在这种情况下注意(ht->num_buckets - 1)8-1=70b111.

并为1919:

'0b11101111111' & '0b111' = '0b111' # 7 the index of 1919
Run Code Online (Sandbox Code Playgroud)

并为333:

'0b101001101' & '0b111' = '0b101' # 5 the index of 333
Run Code Online (Sandbox Code Playgroud)

以及前面的例子:

>>> set([8,2,5,3,6])
set([8, 2, 3, 5, 6])

'0b1000' & '0b100'='0b0' # for 8
'0b110' & '0b100'='0b100' # for 8
Run Code Online (Sandbox Code Playgroud)

*类的哈希函数int:

class int:
    def __hash__(self):
        value = self
        if value == -1:
            value = -2
        return value
Run Code Online (Sandbox Code Playgroud)

  • 在最后给出的示例中,您似乎假设`ht-> num_buckets`等于集合中的项目数.事实并非如此:桶的数量是2的幂,并且通常比集合中的项目数量大得多(实际上,对于要填充的所有或几乎所有桶的哈希冲突都是不利的;启发式是Python的用途是在哈希表变为2/3满时将其扩大. (2认同)