对于生成器输入,是否有替代python的排列?

时间:2017-09-27 17:43:20

标签: python generator permutation itertools infinite

我正在尝试在itertools.permutations中使用无界的生成器,但它似乎不起作用。永远不会创建返回生成器,因为该函数只是永远运行。要明白我的意思,请考虑:

from itertools import count, permutations
all_permutations = permutations(count(1), 4)

我怎么想象这个工作是它产生前4个自然数的所有可能的4长度排列。然后它应该生成前5个自然数的所有可能的4长度排列,没有重复,因此所有这些都必须包含5个。但是,创建all_permutations时会挂起python。

在我从头开始创建自己的功能之前,我想知道是否有另一个库可以做我正在寻找的东西?此外,这里的内置功能不应该能够处理吗?这可能是一个应该解决的错误吗?

编辑:对于一些迭代...

1 2 3 4
1 2 4 3
...
4 3 2 1
1 2 3 5
1 2 5 3
...
5 3 2 1
1 2 4 5
1 2 5 4
...

2 个答案:

答案 0 :(得分:4)

这样的事情:

from itertools import count, permutations

def my_permutations(gen, n=4):
    i = iter(gen)
    population = []
    seen = set()
    while True:
        for p in permutations(population, n):
            if p not in seen:
                yield p
                seen.add(p)
        population.append(next(i))

请注意,内存使用量正在不断增长,但据我所知,没有办法解决这个问题。

更高效的版本:

def my_permutations(gen, n=4):
    i = iter(gen)
    population = []
    while True:
        population.append(next(i))
        *first, last = population
        perms = permutations(first, n-1)
        yield from (p[:i] + (last,) + p[i:] for p in perms for i in range(n))

答案 1 :(得分:3)

好问题!这是一种有效的方法,可以系统地生成它们,而无需重复(并且无需检查):

  1. 首先是第一个n元素的排列;
  2. 然后排列涉及前一个n+1元素n-1;
  3. 然后涉及n+2 nd元素和之前的n-1元素等。
  4. 换句话说,绘制的最后一个元素始终包含在当前批次中。这只保留了消耗的源元素的元组(不可避免,因为我们将继续在排列中使用所有这些元素)。

    正如您所看到的,我稍微简化了实现:我使用base元素初始化n-1而不是第1步,直接进入主循环。

    from itertools import islice, permutations, combinations
    
    def step_permutations(source, n):
        """Return a potentially infinite number of permutations, in forward order"""
    
        isource = iter(source)
        # Advance to where we have enough to get started
        base = tuple(islice(isource, n-1))
    
        # permutations involving additional elements:
        # the last-selected one, plus <n-1> of the earlier ones
        for x in isource:
            # Choose n-1 elements plus x, form all permutations
            for subset in combinations(base, n-1):
                for perm in permutations(subset + (x,), n):
                    yield perm
    
            # Now add it to the base of elements that can be omitted 
            base += (x,)
    

    演示:

    >>> for p in step_permutations(itertools.count(1), 3):
        print(p)
    
    (1, 2, 3)
    (1, 3, 2)
    (2, 1, 3)
    (2, 3, 1)
    (3, 1, 2)
    (3, 2, 1)
    (1, 2, 4)
    (1, 4, 2)
    (2, 1, 4)
    (2, 4, 1)
    (4, 1, 2)
    (4, 2, 1)
    (1, 3, 4)
    (1, 4, 3)
    (3, 1, 4)
    (3, 4, 1)
    (4, 1, 3)
    (4, 3, 1)
    (2, 3, 4)
    (2, 4, 3)
    (3, 2, 4)
    ...