itertools.product:如何提高性能?

时间:2014-10-02 01:20:57

标签: python itertools

我需要生成itertool.permutation生成器列表的产品,并使用以下代码:

def iter_version():
  l = [itertools.permutations(range(10)) for _ in range(10)]
  g = itertools.product(*l)
  for i in g:
    yield i

但这段代码很慢。我的桌面需要16秒。除了告诉我这个功能需要16秒时,cProfile什么也没有显示。

如果我像这样创建一些疯狂的循环:

def for_loop():
  l = [itertools.permutations(range(10)) for _ in range(10)]
  for i0 in l[0]:
    for i1 in l[1]:
      for i2 in l[2]:
        for i3 in l[3]:
          for i4 in l[4]:
            for i5 in l[5]:
              for i6 in l[6]:
                for i7 in l[7]:
                  for i8 in l[8]:
                    for i9 in l[9]:
                      yield (i0, i1, i2, i3, i4, i5, i6, i7, i8, i9)

这几乎是立即运行。

在我的情况下,排列生成器列表不是固定大小,所以我不能使用for循环版本。

2 个答案:

答案 0 :(得分:1)

就像@ DSM的回答所说,itertools.product会将迭代转换为具体的序列。这可以从http://bugs.python.org/issue10109

确认

为了解决这个问题而不将iterable转换为list,我使用了这个函数。请注意,此函数使用递归,因此请在使用前进行测试。

def product(*args):
    if len(args) == 1:
        for i in args[0]:
            yield [i]
    else:
        for i in args[0]:
            for j in product(*args[1:]):
                j.append(i)
                yield j

答案 1 :(得分:0)

尊敬地说,我不相信你的第一个代码需要运行16秒。有(3628800)^ 10,或395940866122425193243875570782668457763038822400000000000000000000,要产生的元素。我可以想象它在一些系统上花了16秒来计算3628800 * 10 = 36288000的排列。 (由于你没有显示你如何调用iter_version,你可能只是在next(iter_version())之后,或者其他什么,我想,尽管如此,有更简单的方法来获取它......)

iter_versionfor_loop之间的真正区别在于itertools.product没有实现笛卡尔积,但 将每个参数转换为a首先列出,列表可以重复迭代。在for_loop中,你正在耗费你的迭代器,所以你的工作几乎没有那么多。

使用较小的案例可能更容易看到,比如说(2,2)而不是(10,10):

>>> list(iter_version())
[((0, 1), (0, 1)), ((0, 1), (1, 0)), ((1, 0), (0, 1)), ((1, 0), (1, 0))]
>>> list(for_loop())
[((0, 1), (0, 1)), ((0, 1), (1, 0))]

如果您在list电话周围添加itertools.permutations,它们会再次相同:

>>> list(for_loop_materialized_list())
[((0, 1), (0, 1)), ((0, 1), (1, 0)), ((1, 0), (0, 1)), ((1, 0), (1, 0))]

如果你真的想要iter_version的结果,我建议你开始想要别的东西。 : - )