包含选择元素的组合(Python)

时间:2013-11-18 18:01:33

标签: python combinations combinatorics

为了制作数字0到x的所有组合的集合,长度为y,我们做:

list_of_combinations=list(combinations(range(0,x+1),y))
list_of_combinations=map(list,list_of_combinations)
print list_of_combinations

这会将结果作为列表列表输出。

例如,x = 4,y = 3:

[[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 2, 3], [0, 2, 4], [0, 3, 4], [1, 2, 3], [1, 2, 4], 
[1, 3, 4], [2, 3, 4]]

我正在尝试执行上述操作,但仅输出预先选择了2个成员的列表。

例如,我想只输出其中包含1和4的组合集。然后输出(对于x = 4,y = 3):

[[0, 1, 4], [1, 2, 4], [1, 3, 4]]

我现在拥有的最佳方法是制作一个y-2长度的列表,其中包含所有数字而没有选择的数字,然后附加所选的数字,但这似乎非常低效。任何帮助赞赏。

*编辑:我正在为大x和y做这个,所以我不能只写出所有组合然后搜索所选元素,我需要找到一个更好的方法。

2 个答案:

答案 0 :(得分:3)

combinations()返回 iterable ,因此在生成列表时循环显示

[list(combo) for combo in combinations(range(x + 1), y) if 1 in combo]

这会产生一个列表,列出符合条件的所有组合。

演示:

>>> from itertools import combinations
>>> x, y = 4, 3
>>> [list(combo) for combo in combinations(range(x + 1), y) if 1 in combo]
[[0, 1, 2], [0, 1, 3], [0, 1, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4]]

另一种方法是生成已移除y - 1的{​​{1}} range(x + 1)组合,然后重新添加1(使用bisect.insort()以避免必须排序之后):

1

然后遍历该生成器:

import bisect

def combinations_with_guaranteed(x, y, *guaranteed):
    values = set(range(x + 1))
    values.difference_update(guaranteed)
    for combo in combinations(sorted(values), y - len(guaranteed)):
        combo = list(combo)
        for value in guaranteed:
            bisect.insort(combo, value)
        yield combo

这不会产生那么多的过滤组合再次丢弃。

对于较大的>>> list(combinations_with_guaranteed(4, 3, 1)) [[0, 1, 2], [0, 1, 3], [0, 1, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4]] >>> list(combinations_with_guaranteed(4, 3, 1, 2)) [[0, 1, 2], [1, 2, 3], [1, 2, 4]] 值和有保证的数字,可能只需使用y即可击败重复的yield sorted(combo + values)次呼叫。

答案 1 :(得分:1)

这应该可以解决问题:

filtered_list = filter(lambda x: 1 in x and 4 in x, list_of_combinations)

为了使你的代码更好(使用更多的生成器),我会使用这个

combs = combinations(xrange(0, x+1), y)
filtered_list = map(list, filter(lambda x: 1 in x and 4 in x, combs))

如果您不需要filtered_list作为列表并且它可以是可迭代的,您甚至可以

from itertools import ifilter, imap, combinations
combs = combinations(xrange(0, x+1), y)
filtered_list = imap(list, ifilter(lambda x: 1 in x and 4 in x, combs))
filtered_list.next()
>  [0, 1, 4]
filtered_list.next()
>  [1, 2, 4]
filtered_list.next()
>  [1, 3, 4]
filtered_list.next()
>  Traceback (most recent call last):
>    File "<stdin>", line 1, in <module>
>  StopIteration