Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python字典中计算冲突_Python_Optimization_Dictionary - Fatal编程技术网

在Python字典中计算冲突

在Python字典中计算冲突,python,optimization,dictionary,Python,Optimization,Dictionary,我第一次在这里发帖,希望我问的问题是正确的 在向Python字典添加元素后,是否可以让Python告诉您添加该元素是否导致冲突?(在找到放置元素的位置之前,冲突解决策略探测了多少个位置?) 我的问题是:我正在使用字典作为一个更大项目的一部分,经过广泛的分析,我发现代码中最慢的部分是处理使用字典实现的稀疏距离矩阵 我使用的键是Python对象的ID,它们是唯一的整数,所以我知道它们都散列到不同的值。但原则上,将它们放在字典中仍可能导致冲突。我不相信字典冲突会让我的程序慢下来,但我想从查询中消除它们

我第一次在这里发帖,希望我问的问题是正确的

在向Python字典添加元素后,是否可以让Python告诉您添加该元素是否导致冲突?(在找到放置元素的位置之前,冲突解决策略探测了多少个位置?)

我的问题是:我正在使用字典作为一个更大项目的一部分,经过广泛的分析,我发现代码中最慢的部分是处理使用字典实现的稀疏距离矩阵

我使用的键是Python对象的ID,它们是唯一的整数,所以我知道它们都散列到不同的值。但原则上,将它们放在字典中仍可能导致冲突。我不相信字典冲突会让我的程序慢下来,但我想从查询中消除它们

例如,给定以下词典:

d = {}
for i in xrange(15000):
    d[random.randint(15000000, 18000000)] = 0
您能让Python告诉您在创建它时发生了多少冲突吗

我的实际代码与应用程序纠缠在一起,但上面的代码生成的字典与我正在使用的字典非常相似

重复:我不认为冲突会减慢我的代码,我只是想通过显示我的字典没有太多冲突来消除这种可能性

谢谢你的帮助

编辑:实现@Winston Ewert解决方案的一些代码:

n = 1500
global collision_count
collision_count = 0

class Foo():

    def __eq__(self, other):
        global collision_count
        collision_count += 1
        return id(self) == id(other)

    def __hash__(self):
        #return id(self) # @John Machin: yes, I know!
        return 1

objects = [Foo() for i in xrange(n)]

d = {}
for o in objects:
    d[o] = 1

print collision_count
请注意,当您在类上定义
\uuuuuu eq\uuuu
时,如果您不同时定义
\uuuu hash\uu
函数,Python会给您一个
TypeError:unhabable instance

它不像我预期的那样运行。如果您有
\uuuuuu hash\uuuuu
函数
return 1
,那么您将获得预期的冲突负载(在我的系统上,n=1500的冲突负载为1125560)。但是对于
返回id(self)
,有0个冲突

有人知道为什么说0碰撞吗

编辑: 我可能已经弄明白了


是不是因为只有当两个对象的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu散列值相同时才会调用
,而不是它们的“压缩版本”(正如@John Machin所说的)?

如果您的键保证是唯一的整数,并且由于Python在键上使用
散列()
,然后,应保证不会发生任何碰撞。

此想法实际上不起作用,请参见问题中的讨论。

快速查看python的C实现可以发现,用于解决冲突的代码并没有计算或存储冲突的数量

但是,它将调用键上的
PyObject\u richcomarebool
,检查它们是否匹配。这意味着每次碰撞都会调用键上的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

因此:

用定义
\uuuu eq\uuu
并在调用计数器时递增计数器的对象替换关键帧。这会更慢,因为跳转到python进行比较会带来开销。然而,它应该能让你知道发生了多少次碰撞


确保使用不同的对象作为键,否则python将采用快捷方式,因为对象始终等于自身。另外,确保对象散列到与原始键相同的值。

简短回答:

不能通过使用随机整数作为dict键来模拟使用对象ID作为dict键。它们有不同的散列函数

碰撞确实会发生。对于“thingy”的几个值来说,“拥有唯一的thingies意味着没有碰撞”是错误的

你不应该担心碰撞

长答案:

一些解释来自:

dict实现为包含2**i条目的表,其中i为整数

听写的内容不超过2/3。因此,对于15000个键,i必须是15,2**i是32768

当o是未定义
\uuuu hash\uuuu()
的类的任意实例时,hash(o)=id(o)不是真的。由于地址可能在低阶3或4位中具有零,因此通过将地址向右旋转4位来构造哈希;请参阅函数
\u Py\u HashPointer

如果低阶位中有很多零,这将是一个问题,因为要访问大小为2**i(例如32768)的表,必须对散列值(通常比该值大得多)进行压缩以适应,而这是通过获取散列值的低阶i(例如15)位非常简单而快速地完成的

因此,碰撞是不可避免的

然而,这并不是恐慌的原因。散列值的剩余位被分解到下一个探测的位置的计算中。需要第三个etc探头的可能性应该很小,尤其是dict的填充量永远不会超过2/3。多个探测的成本通过计算第一个和后续探测的插槽的廉价成本得以降低

下面的代码是一个简单的实验,演示了上面的大部分讨论。它假定dict达到最大大小后会随机访问。在Python2.7.1中,它显示了15000个对象(13.3%)的2000次碰撞

无论如何,底线是你真的应该把注意力转移到别处。碰撞不是你的问题,除非你已经实现了一些非常不正常的方式来获取你的对象的内存。您应该了解您是如何使用dicts的,例如在d中使用
k
或尝试/except,而不是
d。考虑将一个DICT访问为<代码> d[[(x,y)] < /> >而不是两个级别,访问为<代码> d[x] [y] < /代码>。如果你需要帮助,可以问另外一个问题

在Python 2.6上测试后更新

直到Python 2.7才引入旋转地址;有关全面的讨论和基准,请参见。基本结论如下:
>>> n = 15000
>>> i = 0
>>> while 2 ** i / 1.5 < n:
...    i += 1
...
>>> print i, 2 ** i, int(2 ** i / 1.5)
15 32768 21845
>>> probe_mask = 2 ** i - 1
>>> print hex(probe_mask)
0x7fff
>>> class Foo(object):
...     pass
...
>>> olist = [Foo() for j in xrange(n)]
>>> hashes = [hash(o) for o in olist]
>>> print len(set(hashes))
15000
>>> probes = [h & probe_mask for h in hashes]
>>> print len(set(probes))
12997
>>>