加速计算元组

时间:2014-04-10 18:34:23

标签: python performance for-loop counting

对于我正在做的项目,我有兴趣创建符合以下条件的所有元组(i,j,k,z,f)的列表:

  • 所有五个变量都可以在0到343之间变化
  • i == k j == z k == f

到目前为止我想出的是:

z1=set()
for i in xrange(344):
    for j in xrange(344):
        for k in xrange(344):
            for z in xrange(344):
                for f in xrange(344):
                    if f!=k:
                        continue
                    z1.add((i,j,k,z,f))
for i in xrange(344):
    for j in xrange(344):
        for k in xrange(344):
            for z in xrange(344):
                for f in xrange(344):
                    if z!=j:
                        continue
                    if (i,j,k,z,f) not in z1:
                        z1.add((i,j,k,z,f))
for i in xrange(344):
    for j in xrange(344):
        for k in xrange(344):
            for z in xrange(344):
                for f in xrange(344):
                    if k!=i:
                        continue
                    if (i,j,k,z,f) not in z1:
                        z1.add((i,j,k,z,f))

这很慢。我想可能有一种简单的方法来加快速度,我忽略了......任何想法?

5 个答案:

答案 0 :(得分:4)

你需要更多的记忆力。很多很多的记忆。让我们看看第一个条件i == k。满足该条件的元组数是343 ** 4 = 13841287201。 130亿!如果每个元组只需要5*4 = 20个字节的内存,那么该集合所需的仍然是257 GB。而这甚至不是你想要的所有元素。

所以,不,没有简单的出路。如果我要将这个问题优化到可管理的大小,我首先会问mysqlf:我真的需要一个大的列表吗?或者我可以在没有它的情况下解决这个问题吗?

答案 1 :(得分:2)

您是否希望循环次数较少且比较少。

尝试以下操作,可能会更快:

def doit ():
    theRange = range (343)
    for i in theRange:
        for j in theRange:
            for k in theRange:
                cond1 = i == k
                for z in theRange:
                    cond2 = j == z
                    for f in theRange:
                        if cond1 or cond2 or k == f:
                            yield i, j, k, z, f

for x in doit ():
    doSomethingWith (x)

答案 2 :(得分:2)

你真的不需要条件来做到这一点,

利用仅包含每个值之一的集合。

a = set()
for index in xrange(344):
    for i in xrange(344):
        for j in xrange(344):
            for k in xrange(344):
                #i==k
                a.add((index,i,index,j,k))
                #j==z
                a.add((i,index,j,index,k))
                #k==f
                a.add((i,j,index,k,index))

编辑值类型更改为〜我需要更多咖啡

答案 3 :(得分:1)

由于您在尝试构造集合时内存不足,请使用生成器迭代它们而不将它们全部存储起来:

itertools.ifilter(
    lambda x: x[0] == x[2] or x[1] == x[3] or x[2] == x[4],
    itertools.product(xrange(344), repeat=5)
)

当然,迭代仍需要很长时间,因为还有很多工作要做(大约5万亿个值)。稍后会详细介绍。

如果真的出于某种特殊原因需要这个设置,你可以在这个想法的基础上定义一个" lazy"不可变集。在Python 2中:

class MyCollection(collections.Set):
    def __contains__(self, x):
        if not isinstance(x, tuple): return False
        if not len(x) == 5: return False
        if not all(isinstance(y, int) for y in x): return False
        if not all(0 <= y <= 343 for y in x): return False
        return self.match(x)
    @classmethod
    def match(cls, x):
        return (x[0] == x[2] or x[1] == x[3] or x[2] == x[4])
    def __iter__(self):
        return itertools.ifilter(
            self.match,
            itertools.product(xrange(344), repeat=5)
        )
    def __len__(self):
        # um. Left as an exercise for the reader. About 39 billion or so.

z1 = MyCollection()

您还可以通过考虑避免访问每个组合的方法来使迭代更有效。我能想到的最简单的是,如果i!=kj!=z,则f唯一可能的值是k。因此,在这种情况下,不要迭代所有可能的f,只输出可行的那个。

然而,效率最高的可能是:

  • 迭代4个值的所有组合
  • 您还需要添加一个值。这将在结尾标记为f(在这种情况下,它等于k),或者在zk中插入中间(等于ij)。

这些中有420亿,但这确实包含一些重复。我并不认为排除它们非常简单,但我采用的方式是按照从(0,0,0,0)到{(344,344,344,344)的顺序考虑所有正确的4元组。 {1}}。在输出从每个4元组生成的三个5元组中的一个之前,确定是否有任何早期的4元组可以生成它。如果是,那就跳过它。

答案 4 :(得分:1)

稍微调整一下@ Hyboreus的代码:

def legal_tuples():
    r = range(344)
    for i in r:
        for j in r:
            for k in r:
                i_eq_k = i == k
                for z in r:
                    if i_eq_k or j == z:
                        for f in r:
                            yield i,j,k,z,f
                    else:
                        yield i,j,k,z,k