优化的Python字典/负索引存储

时间:2011-03-11 19:59:36

标签: python optimization dictionary indices

this问题的评论提出(我可以看出这是无关紧要的),我现在知道使用字典来查找需要定期查询/访问的数据并不好, speedwise。

我的情况是这样的:

someDict = {}
someDict[(-2, -2)] = something
somedict[(3, -10)] = something else

我将坐标键存储到对象中作为游戏中瓷砖阵列的对象。这些在某些时候会是负面的,所以我不能使用列表或某种稀疏数组(我认为这是术语?)。

我可以:

  • 加快字典查找,所以这不是问题
  • 找一些支持稀疏,负面指数的容器?

我会使用一个列表,但是然后查询将从O(log n)变为O(n)以找到(x,y)处的区域。 (我想我的时间也在这里。)

5 个答案:

答案 0 :(得分:2)

开始
  

加速字典查找,所以这不是问题

字典查找非常快O(1),但是(从你的另一个问题)你不依赖于字典的哈希表查找,你依赖于字典键的线性搜索。

  

找一些支持稀疏负指数的容器?

这不是索引到字典中。元组是一个不可变对象,你整个都是对元组进行哈希处理。字典真的不知道密钥的内容,只知道它们的哈希值。

我会像其他人一样建议您重组数据。

例如,您可以创建封装所需数据的对象,并将它们排列在二叉树中以进行O(n lg n)次搜索。您甚至可以将整个事物包装在一个类中,它将为您提供所需的if foo in Bar:语法。

您可能需要一些协调的结构来完成您想要的任务。这是使用dicts和sets的简化示例(稍微调整用户6502的建议)。

# this will be your dict that holds all the data
matrix = {}
# and each of these will be a dict of sets, pointing to coordinates
cols = {}
rows = {}

def add_data(coord, data)
    matrix[coord] = data
    try:
        cols[coord[0]].add(coord)
    except KeyError:
        # wrap coords in a list to prevent set() from iterating over it
        cols[coord[0]] = set([coord])
    try:
        rows[coord[1]].add(coord)
    except KeyError:
        rows[coord[1]] = set([coord])

# now you can find all coordinates from a row or column quickly
>>> add_data((2, 7), "foo4")
>>> add_data((2, 5), "foo3")
>>> 2 in cols
True
>>> 5 in rows
True
>>> [matrix[coord] for coord in cols[2]]
['foo4', 'foo3']

现在只需将它包装在一个类或一个模块中,你就会离开,而且一如既往,如果你的猜测不够快,那么你的猜测就不够快。

答案 1 :(得分:2)

Python字典非常快,使用整数元组不会成为问题。但是你的用例似乎有时需要进行单坐标检查,并且遍历所有dict当然很慢。

您可以使用三个词典来加速数据结构以获得所需的访问权限,而不是进行线性搜索:

class Grid(object):
    def __init__(self):
        self.data = {}  # (i, j) -> data
        self.cols = {}  # i -> set of j
        self.rows = {}  # j -> set of i

    def __getitem__(self, ij):
        return self.data[ij]

    def __setitem__(self, ij, value):
        i, j = ij
        self.data[ij] = value
        try:
            self.cols[i].add(j)
        except KeyError:
            self.cols[i] = set([j])
        try:
            self.rows[j].add(i)
        except KeyError:
            self.rows[j] = add([i])

    def getRow(self, i):
        return [(i, j, data[(i, j)])
                for j in self.cols.get(i, [])]

    def getCol(self, j):
        return [(i, j, data[(i, j)])
                for i in self.rows.get(j, [])]

请注意,还有许多其他可能的数据结构,具体取决于您要执行的操作,读取频率,更新频率,是否通过矩形查询,是否查找最近的非空单元格等等

答案 2 :(得分:1)

另一种选择是简单地改变指数,使其成为正面。

E.g。如果您的指数是连续的,那么:

...
-2 -> a
-1 -> c
0 -> d
1 -> e
2 -> f
...

只需执行LookupArray [Index + MinimumIndex]之类的操作,其中MinimumIndex是您将使用的最小索引的绝对值。

这样,如果您的最小值是-50,那么它将映射到0. -20将映射到30,依此类推。

编辑:

另一种方法是使用如何使用索引的技巧。定义以下关键功能

Key(n) = 2 * n (n >= 0)
Key(n) = -2 * n - 1. (n < 0)

这将所有正键映射到正偶数索引,将所有负数元素映射到正奇数索引。但这可能不实用,因为如果添加100个负键,则必须将数组扩展200个。

还有一点需要注意:如果您打算进行查找并且键的数量是恒定的(或者非常缓慢地变化),请坚持使用数组。否则,词典根本就不好。

答案 3 :(得分:1)

字典查找非常快。搜索密钥的一部分(例如,行x中的所有图块)是不快的。你可以使用dicts的词典。而不是由2元组索引的单个字典,使用这样的嵌套字典:

somedict = {0: {}, 1:{}}
somedict[0][-5] = "thingy"
somedict[1][4] = "bing"

然后,如果你想要给定“行”中的所有图块,它只是somedict[0]

您需要一些逻辑来在必要时添加辅助词典等等。提示:在标准getitem()类型或setdefault()类型上查看dictcollections.defaultdict

此方法可让您快速访问给定行中的所有切片。如果您想要给定列中的所有切片,它仍然很慢(尽管至少您不需要查看每个单元格,只需查看每一行)。但是,如果需要,您可以通过使用两个dicts(一个在列中,行顺序,另一个在行,列顺序)来解决这个问题。然后更新成为工作量的两倍,这对于大多数磁贴是静态的游戏来说无关紧要,但在任何一个方向上访问都非常容易。

如果您只需要存储数字并且大部分单元格都为0,请查看scipy的稀疏矩阵类。

答案 4 :(得分:0)

使用多维列表 - 通常作为嵌套对象实现。你可以通过一点算术轻松地处理负指数。它可能使用比字典更多的内存,因为某些必须放在每个可能的插槽中(对于空插槽通常为None),但访问将通过简单的索引查找而不是散列来完成就像用字典一样。