在什么情况下,如果我们将排序的数字作为键添加到哈希表中,我们可以期望对哈希进行排序


In what situation, if we add sorted numbers as keys to a hash table, we can expect the hash to be ordered?

PHP中,当我们使用排序数字作为键将元素插入到新的哈希表中时,生成的哈希也会被排序吗?

所以当我们拿到钥匙时,它们会被订购,$a[0], $a[1], $a[2]也会遵循原来的顺序? (虽然当然,键将是该顺序,但值不必如此(。

在PHP中,我们可以指望这一点吗? 在Perl,Python或Ruby中没有这样的行为吗?

Python 有OrderedDict .其他语言也有等效语言。

然而,通常这种行为不能保证基本的哈希类型(例如Python的dict(,因为它需要额外的簿记。

PHP 的数组是一片特殊的雪花;即使您正在使用 PHP,也最好不要养成依赖基本哈希的习惯。

Perl 中的行为记录在键中:

哈希的键以明显的随机顺序返回。实际的随机顺序在 Perl 的未来版本中可能会发生变化,但它保证与值或每个函数产生的顺序相同(假设哈希尚未修改(。从 Perl 5.8.1 开始,出于安全原因,即使在不同的 Perl 运行之间,排序也可能不同(参见 perlsec 中的算法复杂性攻击(。

您可以使用 Tie::IxHash:

这个 Perl 模块实现了 Perl 哈希,这些哈希保留了哈希元素的添加顺序。更改与IxHash中现有键对应的值时,顺序不受影响。这些元素也可以设置为任意提供的顺序。熟悉的Perl数组操作也可以在IxHash上执行。

正如Amber所指出的,集合。OrderedDict是Python工具,保证保持广告顺序。

也就是说,我发现标题中提出的问题很有趣。 Python 的一个实现细节是整数的哈希值是值本身。 由于常规字典(通常是无序的(只是哈希表,因此有时可以将排序的数字添加到字典中,使它们保持排序:

>>> from random import sample
>>> dict.fromkeys(range(5)).keys()
[0, 1, 2, 3, 4]
>>> dict.fromkeys(range(25)).keys()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
>>> dict.fromkeys(range(0,25,2)).keys()
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24]
>>> dict.fromkeys(sorted(sample(range(50), 40))).keys()
[0, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 15, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 47, 49]

此结果很脆弱,不能保证行为。 它依赖于以下属性:

  • 键根据其哈希值模化字典大小(从 8 开始(进行定位
  • 整数
  • 的哈希值是整数本身
  • 当哈希表已满三分之二时,其大小将向上调整四倍(然后开始加倍时最多 50,000(,并重新插入现有的键/值对。

问题:在什么情况下,如果我们将排序数字作为键添加到哈希表中, 我们可以期待哈希被排序吗?

答:排序的数字键在常规字典中保持排序,当且仅当这些值在取模 n 作为字典大小时保持排序,并且如果该条件也适用于作为添加元素创建的每个较小的字典:

  • 取模 8 时,前五个元素 (8 * 2//3( 必须排序。
  • 前 21 个元素 (32* 2//3( 在取模 32 时必须排序。
  • 前八十五个元素(128 * 2//3(在取模块128时必须排序。
  • 等等...

在代码中:

def will_remain_sorted(seq):
    i, n = 0, 8
    while i < len(seq):
        i = n * 2 // 3
        if not sorted(seq[:i], key=lambda x: x%n) == seq[:i]:
            return False
        n *= 4 if n < 50000 else 2
    return True