为什么`myfloat in myset` 变得超级慢?

当我将相同的float值重新插入我的集合几次时,x in s应该花费恒定时间的检查变得非常缓慢。为什么?

计时输出x in s

   0.06 microseconds
   0.09 microseconds
   0.16 microseconds
   0.56 microseconds
   1.00 microseconds
   1.58 microseconds
   2.55 microseconds
   5.98 microseconds
  10.50 microseconds
  24.54 microseconds
  40.39 microseconds
  96.60 microseconds
 160.24 microseconds
 419.08 microseconds
 732.27 microseconds

代码(在线试用!):

from timeit import timeit

s = {float('nan')}
for _ in range(15):
    for _ in [*s]:
        x = float('nan')
        s.add(x)
    time = timeit('x in s', number=1000, globals=globals()) * 1e3
    print('%7.2f microseconds' % time)

回答

因为您正在使用nan,这因打破对__hash__/__eq__合同的幼稚期望而臭名昭著......即:

>>> myset = set()
>>> myset.add(float('nan'))
>>> myset
{nan}
>>> myset.add(float('nan'))
>>> myset
{nan, nan}

发生这种情况是因为:

>>> float('nan') == float('nan')
False

但:

>>> hash(float('nan')) == hash(float('nan'))
True

所以你每次都会发生碰撞,你会看到散列集的行为降级为 O(N),这是最坏的情况,而不是 O(1)。从根本上说,您不会重新插入相同的浮点值

此外,请注意以下行为:

>>> nan = float('nan')
>>> myset = set()
>>> myset.add(nan)
>>> myset.add(nan)
>>> myset
{nan}

尽管:

>>> nan == nan
False

以上是由于优化,对于容器,Python 实际上首先检查身份以避免潜在的昂贵__eq__操作。由于我重新使用了相同的对象,现在它被认为是“相同的值”。


以上是为什么`myfloat in myset` 变得超级慢?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>