鉴于两个项目不能在同一列表中,我如何获得列表的所有组合?

时间:2019-09-01 20:29:27

标签: python algorithm

我当前正在尝试查找数字列表中所有可能的集合,其中集合中的两个或多个元素不能在同一集合中。

例如,我有一个原始列表[1、2、3、4、5、6],而我有一组列表[{1,2},{3,4}],这意味着1和2不能在同一组中,并且3和4不能在同一组中。

鉴于这两个输入,程序的结果应为:

{1, 6, 3, 5}
{1, 6, 4, 5}
{2, 6, 3, 5}
{2, 6, 4, 5}

顺序与最终输出无关紧要。

编辑:我重写了实现(这次没有递归)。现在我收到一条错误消息,提示我无法从列表中删除某项内容,因为它不存在...

def schedules(overlaps, complete):
print(complete)
final = complete.copy()
print(final)
for sch in complete:
    print(sch)
    for over in overlaps:
        if (over[0] in sch) and (over[1] in sch):
            print("This is an overlap!!!")
            final.remove(sch)
return final

这是上面代码的错误和输出:

[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 3, 6), (1, 2, 4, 5), (1, 2, 4, 
6), (1, 2, 5, 6), (1, 3, 4, 5), (1, 3, 4, 6), (1, 3, 5, 6), (1, 4, 
5, 6), (2, 3, 4, 5), (2, 3, 4, 6), (2, 3, 5, 6), (2, 4, 5, 6), (3, 
4, 5, 6)]

[(1, 2, 3, 4), (1, 2, 3, 5), (1, 2, 3, 6), (1, 2, 4, 5), (1, 2, 4, 
6), (1, 2, 5, 6), (1, 3, 4, 5), (1, 3, 4, 6), (1, 3, 5, 6), (1, 4, 
5, 6), (2, 3, 4, 5), (2, 3, 4, 6), (2, 3, 5, 6), (2, 4, 5, 6), (3, 
4, 5, 6)]
(1, 2, 3, 4)
This is an overlap!!!
This is an overlap!!!
Traceback (most recent call last):
  File "schedule.py", line 24, in <module>
    result = schedules(overlaps, list(comb))
  File "schedule.py", line 19, in schedules
    final.remove(sch)
ValueError: list.remove(x): x not in list    

编辑:添加一个尝试,除final.remove(sch)周围的块外,消除了该错误,但如下面的注释所述,如果重叠集中有两个以上元素,则此代码将不起作用。例如:如果现在重叠为[{1,2},{3,4,5}],则输出应为:

{1,6,3}
{1,6,5}
{1,6,4}
{1,6,5}
{2,6,3}
{2,6,5}
{2,6,4}
{2,6,5}

6 个答案:

答案 0 :(得分:1)

这应该与任意数量的任意长度的集合一起工作,一次从列表中仅删除每个集合中的1个元素。下面的两个版本产生相同的结果,第二个版本使用单个列表理解,虽然简洁,但有点难以理解。

首先,它过滤集合列表以仅获取那些原始列表的子集。然后,它使用itertools.product从每个集合中使用1个元素得出所有组合,然后针对每个结果从原始列表中删除那些元素。

from itertools import product

l = [1, 2, 3, 4, 5, 6]
test_sets = [{1, 2}, {3, 4}]

result = []
subsets = [u for t in test_sets for u in combinations(t, 2) if set(u).issubset(set(l))]
for s in set(product(*subsets)) if len(test_sets) > 1 else subsets:
    result.append({r for r in l if r not in s})
print(result)

结果

[{2, 4, 5, 6}, {2, 3, 5, 6}, {1, 4, 5, 6}, {1, 3, 5, 6}]

答案 1 :(得分:1)

建议:

  • 从重叠列表开始,并为每个集合仅使用一个元素来创建所有排列。
  • 将初始列表中未包含在重叠中的元素添加到每个结果列表中。

在python中,这基本上可以归结为2行,适用于任意数量的任意长度的集合

from itertools import product

init_list =  [1, 2, 3, 4, 5, 6]
overlaps = [{1,2}, {3,4}]

# the 2 lines:
rest = tuple(el for el in init_list if not any(el in ol for ol in overlaps))
[unique + rest for unique in product(*overlaps) if all(u in init_list for u in unique)]
Out[7]: [(1, 3, 5, 6), (1, 4, 5, 6), (2, 3, 5, 6), (2, 4, 5, 6)]

答案 2 :(得分:0)

此代码有效:

def schedules(overlaps, complete):
    final = complete.copy()
    for sch in complete:
        for over in overlaps:
            if (over[0] in sch) and (over[1] in sch):
                try:
                   final.remove(sch)
                except:
                   continue
    return final

上述问题中的错误是由于我试图删除第一个列表两次而导致的,因为其中有两个重叠。我尝试了一下,除了障碍物。

我想知道是否有更好或更多的“ pythonic”方法来做到这一点。让我知道您是否有任何改进!

答案 3 :(得分:0)

这应该可以解决问题:

from itertools import combinations  # to fill the rest of the sequence

# define a recursive function
# it begins to fill the sequences with the mutually exclusive sets
# but also tries to skip them
# it tries to construct only valid combinations
# so it should also perform for larger sets of numbers
def create_sequences(start_sequence, to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=None):
    # leave it up to the user if he want's to pass a list
    # if not just create one but for recursive calls just 
    # reuse the list to avoid garbage collection
    result= collect_sequences_in if collect_sequences_in is not None else list()
    curr_len= len(start_sequence)
    if curr_len == to_length:
        # well this is just for safety
        result.append(start_sequence)
        return result
    # create a working copy, so we can remove one item
    # (one set of elements which are mutually exclusive)
    # so we can pass it to the next recursion if needed
    # without spoiling anything
    mutual_exclusive_sets= list(mutual_exclusive_sets)
    if len(mutual_exclusive_sets) > 0:
        # there are mutually exclusive sets left, so grab
        # one, during this method call we will just work
        # with that set, adding 0 or one elements to the
        # sequence and leaving the rest up to a subsequent 
        # call (if adding one element doesn't complete 
        # the sequence)
        mutual_exclusive_set= mutual_exclusive_sets.pop()
        for value in mutual_exclusive_set:
            if value in start_sequence:
                # that may not be (should only happen if mual_exculsive_sets overlap)
                return result
        # ok so now call the function with the same sequence
        # after removing the one set (this is the case in which
        # we don't take any value from the set --> in your example
        # that wouldn't be necessary since it will not produce
        # a complete sequence and skip this anyways ;-)
        create_sequences(list(start_sequence), to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=result)
        # now the case that we take exactly one element from the set
        # and add it to the sequence
        for value in mutual_exclusive_set:
            work_sequence= start_sequence + [value]
            if len(work_sequence) == to_length:
                result.append(work_sequence)
            else:
                create_sequences(work_sequence, to_length, all_numbers, mutual_exclusive_sets, collect_sequences_in=result)
    elif to_length - curr_len <= len(all_numbers):
        # no mutual exclusive sets left, so now add from all_numbers
        for tup in combinations(all_numbers, to_length - curr_len):
            result.append(start_sequence + list(tup))
    else:
        # we would have to fill the sequence with items of all_numbers
        # but there are no sufficient elements, so skip this step and
        # leave result as it is (this was a dead-end --> like if we
        # chose to skip one of the mutually exclusive sets in your example
        # data --> but e.g. if you run the same with to_length=3 it is relevant)
        pass
    return result

用于以下设置:

all_numbers= [1, 2, 3, 4, 5, 6]
mutual_exclusive= [{1, 2}, {3, 4}]

all_numbers_tmp= list(all_numbers)
for me in mutual_exclusive:
    for n in me:
        all_numbers_tmp.remove(n)


create_sequences([], 4, all_numbers_tmp, mutual_exclusive)

它返回:

Out[27]: [[3, 1, 5, 6], [3, 2, 5, 6], [4, 1, 5, 6], [4, 2, 5, 6]]

答案 4 :(得分:0)

这是使用combinations模块中的itertools以及allany函数的一种更简单的方式来获得所需的输出:

from itertools import combinations

def get_combs(ranges, forbidden, r):
    for comb in combinations(ranges, r):
        if not any(all(k in comb for k in elm) for elm in forbidden):
            yield set(comb)


ranges = range(1, 7)
forbidden = [{1, 2}, {3, 4}]
r = 4

combs = list(get_combs(ranges, forbidden, r))
print(combs)

输出:

[{1, 3, 5, 6}, {1, 4, 5, 6}, {2, 3, 5, 6}, {2, 4, 5, 6}]

答案 5 :(得分:0)

您可以对生成器使用递归。此解决方案首先找到所需长度的所有组合,然后根据匹配的set进行过滤:

data, d1 = [1, 2, 3, 4, 5, 6], [{1,2}, {3,4}]
l = len([i for b in d1 for i in b])
def _filter(_d):
  return all(len(_d&c) < 2 for c in d1)

def combo(d, c = []):
  if len(c) == l:
    yield c
  else:
    for i in filter(lambda x:x not in c, d):
      yield from combo(d, c+[i])

r = [i for i in combo(data) if _filter(set(i))]

输出:

[[1, 3, 5, 6], [1, 3, 6, 5], [1, 4, 5, 6], [1, 4, 6, 5], [1, 5, 3, 6], [1, 5, 4, 6], [1, 5, 6, 3], [1, 5, 6, 4], [1, 6, 3, 5], [1, 6, 4, 5], [1, 6, 5, 3], [1, 6, 5, 4], [2, 3, 5, 6], [2, 3, 6, 5], [2, 4, 5, 6], [2, 4, 6, 5], [2, 5, 3, 6], [2, 5, 4, 6], [2, 5, 6, 3], [2, 5, 6, 4], [2, 6, 3, 5], [2, 6, 4, 5], [2, 6, 5, 3], [2, 6, 5, 4], [3, 1, 5, 6], [3, 1, 6, 5], [3, 2, 5, 6], [3, 2, 6, 5], [3, 5, 1, 6], [3, 5, 2, 6], [3, 5, 6, 1], [3, 5, 6, 2], [3, 6, 1, 5], [3, 6, 2, 5], [3, 6, 5, 1], [3, 6, 5, 2], [4, 1, 5, 6], [4, 1, 6, 5], [4, 2, 5, 6], [4, 2, 6, 5], [4, 5, 1, 6], [4, 5, 2, 6], [4, 5, 6, 1], [4, 5, 6, 2], [4, 6, 1, 5], [4, 6, 2, 5], [4, 6, 5, 1], [4, 6, 5, 2], [5, 1, 3, 6], [5, 1, 4, 6], [5, 1, 6, 3], [5, 1, 6, 4], [5, 2, 3, 6], [5, 2, 4, 6], [5, 2, 6, 3], [5, 2, 6, 4], [5, 3, 1, 6], [5, 3, 2, 6], [5, 3, 6, 1], [5, 3, 6, 2], [5, 4, 1, 6], [5, 4, 2, 6], [5, 4, 6, 1], [5, 4, 6, 2], [5, 6, 1, 3], [5, 6, 1, 4], [5, 6, 2, 3], [5, 6, 2, 4], [5, 6, 3, 1], [5, 6, 3, 2], [5, 6, 4, 1], [5, 6, 4, 2], [6, 1, 3, 5], [6, 1, 4, 5], [6, 1, 5, 3], [6, 1, 5, 4], [6, 2, 3, 5], [6, 2, 4, 5], [6, 2, 5, 3], [6, 2, 5, 4], [6, 3, 1, 5], [6, 3, 2, 5], [6, 3, 5, 1], [6, 3, 5, 2], [6, 4, 1, 5], [6, 4, 2, 5], [6, 4, 5, 1], [6, 4, 5, 2], [6, 5, 1, 3], [6, 5, 1, 4], [6, 5, 2, 3], [6, 5, 2, 4], [6, 5, 3, 1], [6, 5, 3, 2], [6, 5, 4, 1], [6, 5, 4, 2]]