关于哈希:访问Python字典的时间复杂度

Time complexity of accessing a Python dict

我正在编写一个简单的python程序。

我的程序似乎受到了字典的线性访问,即使算法是二次型的,它的运行时间也呈指数增长。我用字典来记忆价值观。这似乎是一个瓶颈。

我要散列的值是点的元组。每个点是:(x,y),0<=x,y<=50字典中的每个键是:一个2-5点的元组:((x1,y1),(x2,y2),(x3,y3),(x4,y4)

读钥匙的频率比写钥匙的频率高很多。

对于这种输入,python dict会遭受线性访问时间,这是正确的吗?

据我所知,集合有保证的对数访问时间。如何在python中使用集合(或类似的东西)模拟dict?

根据要求进行编辑,以下是记忆功能的(简化)版本:

1
2
3
4
5
6
7
8
def memoize(fun):
    memoized = {}
    def memo(*args):
        key = args
        if not key in memoized:
            memoized[key] = fun(*args)
        return memoized[key]
    return memo


见时间复杂性。python dict是一个散列映射,因此,如果散列函数不好并导致大量冲突,那么它的最坏情况是o(n)。但是,这是非常罕见的情况,因为添加的每个项都具有相同的哈希值,因此添加到同一个链中,这对于主要的Python实现来说是极不可能的。平均时间复杂性当然是O(1)。

最好的方法是检查并查看正在使用的对象的哈希值。cpython dict使用int pyobject_hash(pyobject*o),它相当于hash(o)

在快速检查之后,我还没有找到两个哈希值相同的元组,这表明查找是O(1)

1
2
3
4
5
6
7
l = []
for x in range(0, 50):
    for y in range(0, 50):
        if hash((x,y)) in l:
            print"Fail:", (x,y)
        l.append(hash((x,y)))
print"Test Finished"

代码板(24小时可用)


如果您提供示例代码和数据,那么就更容易提出建议。

访问字典不太可能是一个问题,因为该操作平均为O(1),而O(n)是分摊的最坏情况。内置的哈希函数可能会与您的数据发生冲突。如果您遇到内置哈希函数的问题,可以提供自己的哈希函数。

Python's dictionary implementation
reduces the average complexity of
dictionary lookups to O(1) by
requiring that key objects provide a
"hash" function. Such a hash function
takes the information in a key object
and uses it to produce an integer,
called a hash value. This hash value
is then used to determine which
"bucket" this (key, value) pair should
be placed into.

您可以覆盖类中的哈希方法来实现如下自定义哈希函数:

1
2
def __hash__(self):    
    return hash(str(self))

根据数据的实际外观,您可能能够找到比标准函数冲突更少的更快的哈希函数。然而,这不太可能。有关更多信息,请参见字典键上的python wiki页面。


你不正确。dict访问不太可能是您的问题。几乎可以肯定它是O(1),除非您有一些非常奇怪的输入或非常糟糕的散列函数。从应用程序中粘贴一些示例代码,以便更好地进行诊断。


My program seems to suffer from linear access to dictionaries, its run-time grows exponentially even though the algorithm is quadratic.

I use a dictionary to memoize values. That seems to be a bottleneck.

这是你记忆方法中的一个错误的证据。


要回答您的特定问题:

问题1:"是否正确,python dict使用此类输入时会出现线性访问时间?"

A1:如果您的意思是平均查找时间是O(N),其中N是dict中的条目数,那么您很可能是错的。如果您是正确的,那么Python社区非常希望知道您在什么情况下是正确的,这样可以减轻或至少警告问题。"示例"代码和"简化"代码都不是有用的。请显示重现问题的实际代码和数据。代码应该使用诸如dict项的数量和每个p的dict访问数之类的东西进行检测,其中p是键中的点数(2<=p<=5)。

问题2:"据我所知,集合有保证的对数访问时间。如何在python中使用集合(或类似的东西)模拟听写?"

A2:集合在什么上下文中保证了对数访问时间?对于Python实现没有这样的保证。最近的cpython版本实际上使用了一个缩减的dict实现(仅限键,没有值),因此期望是平均O(1)行为。你怎么能用任何语言的集合或类似的东西来模拟口述呢?简短的回答:如果您想要除dict.has_key(key)之外的任何功能,则非常困难。


正如其他人指出的,在Python中访问dict很快。考虑到它们的中心作用,它们可能是语言中最好的涂油数据结构。问题出在别处。

你在记忆多少个元组?你考虑过内存占用吗?也许您将所有的时间都花在内存分配器或分页内存上。