Python:大型int键的快速字典

时间:2011-05-06 12:09:39

标签: python optimization dictionary int biginteger

我有一个> 10.000个int项目列表。物品的价值可以非常高,最高可达10 ^ 27。现在我想创建所有项目对并计算它们的总和。然后我想寻找具有相同总和的不同对。

例如:

l[0] = 4
l[1] = 3
l[2] = 6
l[3] = 1
...

pairs[10] = [(0,2)] # 10 is the sum of the values of l[0] and l[2]
pairs[7] = [(0,1), (2,3)] # 7 is the sum of the values of l[0] and l[1] or l[2] and l[3]
pairs[5] = [(0,3)]
pairs[9] = [(1,2)]
...

我正在寻找pairs[7]的内容。它给了我两对具有相同值的总和。

我已经实现了如下 - 我想知道它是否可以更快地完成。目前,对于10,000个物品,在快速机器上需要> 6小时。 (正如我所说,l的值,因此pairs的键值最多为10 ^ 27。)

l = [4,3,6,1]
pairs = {}
for i in range( len( l  )  ):
    for j in range(i+1, len( l ) ):
        s = l[i] + l[j]
        if not s in pairs:
            pairs[s] = []
        pairs[s].append((i,j))

# pairs = {9: [(1, 2)], 10: [(0, 2)], 4: [(1, 3)], 5: [(0, 3)], 7: [(0, 1), (2, 3)]}

编辑我想添加一些背景,正如Simon Stelling所说。

目标是找到像

这样的形式类比
lays : laid :: says : said

之类的单词列表中
[ lays, lay, laid, says, said, foo, bar ... ]

如果analogy(a,b,c,d),我已经有True函数a : b :: c : d。但是,我需要检查从列表中创建的所有可能的四元组,这将是O((n ^ 4)/ 2)的复杂性。

作为预过滤器,我想使用char-count属性。它表示每个字符在(a,d)和(b,c)中具有相同的计数。例如,在“layssaid”中我们有2个,所以我们在“laidsays”中做了

所以直到现在的想法是

  • 为每个单词创建一个“char count vector”并将其表示为整数(列表l中的项目)
  • pairs中创建所有配对,并查看是否存在“配对群集”,即特定字符数向量总和的多个配对。

它有效,但速度很慢。复杂性降到O((n ^ 2)/ 2)左右,但这仍然很多,特别是字典查找和插入经常完成。

4 个答案:

答案 0 :(得分:4)

有一些简单的优化,例如在局部变量中缓存常量值并使用xrange代替range

pairs = {}
len_l = len(l)
for i in xrange(len_l):
    for j in xrange(i+1, len_l):
        s = l[i] + l[j]
        res = pairs.setdefault(s, [])
        res.append((i,j))

然而,不预先计算列表而是在概念级别优化方法可能更为明智。你想要实现的内在目标是什么?你真的只是想计算你做的吗?或者你打算将这个结果用于别的东西?那是什么东西?

答案 1 :(得分:1)

只是一个提示。看看itertools.combinations

这不是你想要的(因为它存储了一对值,而不是索引),但它可以是一个起始代码:

from itertools import combinations
for (a, b) in combinations(l, 2):
    pairs.setdefault(a + b, []).append((a, b))

答案 2 :(得分:0)

SimonStelling的上述评论是正确的;生成所有可能的对只是从根本上来说很慢,除了改变你的算法之外你无能为力。 itertools使用的正确函数是product;并且你可以通过不创建额外的变量或做不必要的列表索引来获得一些小的改进,但是在底层,它们仍然都是O(n ^ 2)。我就是这样做的:

from itertools import product
l = [4,3,6,1]
pairs = {}
for (m,n) in product(l,repeat=2):
    pairs.setdefault(m+n, []).append((m,n))

答案 3 :(得分:0)

最后,我提出了自己的解决方案,平均只花费了一半的计算时间。

基本思想:我首先收集列表中的所有总和,而不是读取和写入不断增长的字典n ^ 2次。然后我对列表进行排序。在排序列表中,我然后查找相同的相邻项目。

这是代码:

from operator import itemgetter

def getPairClusters( l ):

    # first, we just store all possible pairs sequentially
    # clustering will happen later
    pairs = []

    for i in xrange( len( l)  ):
        for j in xrange(i+1, len( l ) ):
            pair = l[i] + l[j]
            pairs.append( ( pair, i, j ) )
    pairs.sort(key=itemgetter(0))

    # pairs = [ (4, 1, 3), (5, 0, 3), (7, 0, 1), (7, 2, 3), (9, 1, 2), (10, 0, 2)]
    # a list item of pairs now contains a tuple (like (4, 1, 3)) with
    # * the sum of two l items: 4
    # * the index of the two l items: 1, 3

    # now clustering starts
    # we want to find neighbouring items as
    # (7, 0, 1), (7, 2, 3)
    # (since 7=7)

    pairClusters = []

    # flag if we are within a cluster
    # while iterating over pairs list
    withinCluster = False

            # iterate over pair list
    for i in xrange(len(pairs)-1):
        if not withinCluster:
            if pairs[i][0] == pairs[i+1][0]:
                # if not within a cluster
                # and found 2 neighbouring same numbers:
                # init new cluster
                pairCluster = [ ( pairs[i][1], pairs[i][2] ) ]
                withinCluster = True
        else:
            # if still within cluster
            if pairs[i][0] == pairs[i+1][0]:
                pairCluster.append( ( pairs[i][1], pairs[i][2] ) )
            # else cluster has ended
            # (next neighbouring item has different number)
            else:
                pairCluster.append( ( pairs[i][1], pairs[i][2] ) )
                pairClusters.append(pairCluster)
                withinCluster = False

    return pairClusters

l = [4,3,6,1]

print getPairClusters(l)