【问题标题】:Optimised Python dictionary / negative index storage优化Python字典/负索引存储
【发布时间】:2011-07-13 18:11:38
【问题描述】:

this 问题的 cmets 提出(我可以看到这无关紧要),我现在意识到使用字典来获取需要定期查询/访问的数据并不好,速度很快。

我有这样的情况:

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

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

我可以:

  • 加快字典查找速度,因此这不是问题
  • 找到某种支持稀疏负索引的容器?

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

【问题讨论】:

  • dicts 对键的元组中的负键或负数没有问题。您担心哪种访问模式性能不佳?
  • 这不是我担心的负键问题;上一个问题告诉我‘一般字典没有为此优化;如果你需要效率,你应该重组你的数据,这样你就不需要这样做了。 '
  • 我认为您的问题与您链接指向的问题无关,除非您想访问例如第一个索引元素是某个值的所有项目。 “对需要定期查询/访问的数据使用字典不好”在一般情况下肯定是不正确的;事实上,字典访问通常在时间上是恒定的。
  • "this" 在该评论中的意思是“检查索引元组的 part”,而不是“使用元组作为键”。对于二维格的表示,您通常可以使用“密集”数组(如果格不是太大)或字典。
  • 我现在可以看出它是多么无关紧要了,谢谢。但是,如果我的原始帖子陷入某种混乱,我就无法真正编辑它。我将离开它;我希望我最初的想法会得到体现。

标签: python optimization dictionary indices


【解决方案1】:

开始

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

字典查找非常快 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']

现在只需将其包装在一个类或模块中,你就会离开,并且一如既往,如果它不够快,在你猜测之前进行配置和测试。

【讨论】:

  • 啊,关于速度我没这么看。至于您的重组建议,二叉树将如何工作?我对它们的工作原理有了基本的了解(并且我之前已经实现过它们),但是它是如何工作的呢?我根本不知道我怎么会有一个明确的根或树结构。
  • 我真的需要一个用例,因为我不确定你在这里做什么。树不需要静态根(请参阅en.wikipedia.org/wiki/Binary_heap)。看起来您也可以使用多维数组,但是您的问题是您不知道坐标的域吗?
  • 我认为域是无限的——也就是说,它们可以扩展到各个方向的技术极限。我离开了多维数组,因为我不能说“将所有内容移动 100”,因为没有明确的负限制。同样,也不能保证坐标是完全连续的。至于用例,它将用于在基于图块的游戏中存储 NxN 块。
  • 我想我可以看到二叉树方法,但我不知道它对于按排序依据的另一个维度进行搜索的效果如何。
  • 二叉树只是另一种数据结构的一个例子。你可能需要结合几个结构来做你想做的事(我会添加另一个想法)。
【解决方案2】:

另一种选择是简单地移动索引,使其为正数。

例如如果您的索引是这样连续的:

...
-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。

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

【讨论】:

  • 如果(就是这种情况)我的负数不断增加怎么办?那么我不是每次都必须将所有内容都移动 n 吗?
【解决方案3】:

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

您可以使用三个字典来加快数据结构的访问速度,而不是进行线性搜索:

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, [])]

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

【讨论】:

    【解决方案4】:

    字典查找速度非常快。搜索部分键(例如第 x 行中的所有图块)并不快。你可以使用字典的字典。不要使用 2 元组索引的单个 dict,而是使用这样的嵌套 dict:

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

    如果您想要给定“行”中的所有图块,则只需 somedict[0]

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

    这种方法使您可以快速访问给定行中的所有图块。如果您想要给定列中的所有图块,它仍然很慢(尽管至少您不需要查看每个单元格,只需查看每一行)。但是,如果需要,您可以通过使用两个 dicts 来解决这个问题(一个按列、行顺序,另一个按行、列顺序)。然后,更新工作量会增加一倍,这对于大多数图块都是静态的游戏来说可能无关紧要,但无论从哪个方向访问都非常容易。

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

    【讨论】:

    • 这个两层字典的想法看起来不错。我可能会重构这个设计;谢谢!
    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 2016-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      • 2012-08-03
      • 2018-06-11
      • 1970-01-01
      相关资源
      最近更新 更多