在什么情况下,如果我们将排序的数字添加为哈希表的键,我们可以期望哈希被排序?

时间:2012-05-02 00:47:27

标签: php python ruby perl hash

在PHP中,当我们使用排序的数字作为键将元素插入到新的哈希表中时,是否也会对所得到的哈希进行排序?

因此,当我们获得密钥时,它们将被订购,而$a[0], $a[1], $a[2]也将遵循原始订单? (虽然肯定,键将是那个顺序,但值不一定是。)

在PHP中,我们可以依靠它吗?在Perl,Python或Ruby中没有这样的行为吗?

3 个答案:

答案 0 :(得分:3)

Python有OrderedDict。其他语言也有等价物。

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

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

答案 1 :(得分:2)

Perl中的行为记录在keys

  

散列的键以明显随机的顺序返回。实际的随机顺序在未来的Perl版本中可能会发生变化,但保证与值或每个函数产生的顺序相同(假设散列未被修改)。从Perl 5.8.1开始,出于安全原因,即使在不同的Perl运行之间,排序也可能不同(参见Algorithmic Complexity Attacks in perlsec)。

您可以使用Tie::IxHash

  

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

答案 2 :(得分:1)

正如Amber所指出的, collections.OrderedDict 是保证插入顺序的Python工具。

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

>>> 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]

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

  • 键根据它们的散列值来定位dict大小(从8开始)
  • 整数的哈希值是整数本身
  • 当哈希表满三分之一时,它会向上调整大小四倍(当它开始加倍时高达50,000)并重新插入现有的键/值对。
  

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

答案:排序的数字键在常规字典中保持排序,当且仅当这些值按照 n 的模式对字典的大小进行排序时才会排序对于添加元素时创建的每个较小的词典也适用:

  • 前五个元素(8 * 2 // 3)必须按模8进行排序。
  • 前32个元素(32 * 2 // 3)必须按模32进行排序。
  • 当采用模块128时,必须对前八十五个元素(128 * 2 // 3)进行排序。
  • 等......

在代码中:

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