访问Python dict的时间复杂度

x10*_*x10 31 python hash complexity-theory dictionary

我正在编写一个简单的Python程序.

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

我正在散列的值是点的元组.每个点是:(x,y),0 <= x,y <= 50
字典中的每个键是:2-5点的元组:((x1,y1),(x2,y2),(x3, Y3),(X4,Y4))

密钥的读取次数比写入次数多很多次.

我是否认为python dicts受到这些输入的线性访问时间的影响?

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

编辑根据请求,这是memoization函数的(简化)版本:

def memoize(fun):
    memoized = {}
    def memo(*args):
        key = args
        if not key in memoized:
            memoized[key] = fun(*args)
        return memoized[key]
    return memo
Run Code Online (Sandbox Code Playgroud)

Yac*_*oby 52

请参阅时间复杂性.python dict是一个hashmap,因此如果hash函数不好并导致大量冲突,则最坏的情况是O(n).然而,这是一种非常罕见的情况,其中添加的每个项目都具有相同的哈希值,因此被添加到同一个链中,对于主要的Python实现来说不可能.平均时间复杂度当然是O(1).

最好的方法是检查并查看正在使用的对象的哈希值.的CPython的字典使用INT PyObject_Hash(的PyObject*O) ,其是相当于hash(o).

经过快速检查后,我还没有设法找到两个散列为相同值的元组,这表明查找是O(1)

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"
Run Code Online (Sandbox Code Playgroud)

CodePad(24小时可用)

  • 那个小范围不相关...... OP 没有使用 `(x, y)` 点作为键——他使用的是 `((x0,y0),(x1, y1))` 到 `((x0) ,y0), ..., (x4, y4))`。他有 `sum(51**(n*2) for n in range(2,6))`(即 119088209375236404)可能的键,而不是 `51**2` (2认同)

Eli*_*sky 8

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

  • 要求示例代码并不粗鲁。字典访问*是*几乎总是O(1),因此我们需要查看示例代码以建议其他可能的瓶颈。 (30认同)

Jam*_*son 8

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

访问字典不太可能成为问题,因为该操作平均O(1),并且 O(N) 分摊最坏情况。内置散列函数可能会遇到数据冲突。如果您在使用内置散列函数时遇到问题,您可以提供自己的散列函数。

Python 的字典实现通过要求键对象提供“散列”函数将字典查找的平均复杂度降低到 O(1)。这样的散列函数获取键对象中的信息并使用它生成一个整数,称为散列值。然后使用该散列值来确定应将此(键、值)对放入哪个“存储桶”。

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

def __hash__(self):    
    return hash(str(self))
Run Code Online (Sandbox Code Playgroud)

根据您的数据的实际情况,您可能能够提出一个比标准函数具有更少冲突的更快的哈希函数。然而,这不太可能。有关更多信息,请参阅字典键上Python Wiki 页面


Joh*_*hin 6

要回答您的具体问题:

问题 1:
“我是否正确地认为 Python dicts 会受到此类输入的线性访问时间的影响?”

A1:如果你的意思是平均查找时间是 O(N),其中 N 是字典中的条目数,那么你很可能是错的。如果您是正确的,Python 社区非常想知道您在什么情况下是正确的,以便可以减轻或至少警告问题。“示例”代码和“简化”代码都没有用。请显示重现问题的实际代码和数据。代码应该配备诸如每个 P 的 dict 项目数和 dict 访问次数之类的东西,其中 P 是键中的点数 (2 <= P <= 5)

Q2:
“据我所知,集合保证了对数访问时间。如何在 Python 中使用集合(或类似的东西)模拟字典?”

A2:集合在什么情况下有保证的对数访问时间?Python 实现没有这样的保证。最近的 CPython 版本实际上使用了一个精简的 dict 实现(只有键,没有值),所以期望是平均 O(1) 行为。你怎么能用任何语言的集合或类似的东西来模拟字典?简短回答:如果您想要任何超出dict.has_key(key).