生成带有递归的组合以及跳过或删除项目

时间:2019-04-23 12:21:25

标签: python

我在python中有这样的元组数组:

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

我需要生成给定长度的所有组合,但是要有一定条件,元组中总是只有两个相同的数字。

对于此示例,在开始生成组合时,我需要先添加(1,2)然后再添加(2,3),但是之后我不能添加(2,4),因为数字2已经使用了2次在[(1,2),(2,3)]中,因此我需要跳过或删除起始数组以下项(包含数字2):(2,4),(2,5),(2,6),(3,2),然后继续进行生成。给定长度3的第一个组合为[(1,2),(2,3),(3,1)],第二个为[(1,2),(2,3),(3,4)],然后为[(1,2),(2,4),(3,1)],依此类推。

有人可以帮我怎么做吗?

4 个答案:

答案 0 :(得分:1)

您可以将递归与生成器一起使用:

from collections import Counter
def groups(d, l, c = []):
  if l == len(c):
    yield c
  else:
    for i in d:
      if i not in c:
        _c = Counter([j for k in [*c, i] for j in k])
        if all(j < 3 for j in _c.values()):
           yield from groups(d, l, c+[i])

data = [(1,2),(2,3),(2,4),(2,5),(2,6),(3,1),(3,2),(3,4)]
result = list(groups(data, 3))
final_result = [a for i, a in enumerate(result) if all(any(c not in h for c in a) for h in result[:i])]

输出:

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

答案 1 :(得分:0)

from random import sample

# Define variables

# All possible elements
all_elements = [1,2,3,4,5,6,7]

# Current possible free elements
possible_elements = {e: 0 for e in all_elements}

# Max number of element usage
MAX = 2

# Result length
N = 5

# Tuple length
L = 2

# Result
result = []

for _ in range(N):
    # Check if we can't construct a new tuple
    try:
        # Randomly get free elements
        t = sample(possible_elements.keys(), L)
    except ValueError:
        break
    # Add tuple to result
    result.append(t)
    # Check elements
    for element in t:
        # List for delete non-free elements
        elements_to_delete = []
        # Check if we can't use the element (it is filled to MAX)
        possible_elements[element] += 1
        if possible_elements[element] == MAX:
            # If yes, add it to to-delete list
            elements_to_delete.append(element)
        # Delete all non-free elements
        for e in elements_to_delete:
            del possible_elements[e]
result

返回:

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

答案 2 :(得分:0)

有几种方法可以做到这一点(大多数方法效率更高),但是只要您的原始元组数组很短,我认为您应该坚持概念上最简单的方法。那可能是在3个for循环中创建所有置换(从元组数组中获取i,j,k)(使用if条件检查i,j和k是否不相同)并将其存储在字典中(键可能是数组中用_分隔的索引)。 最后,您检查字典中是否有任何禁止的组合(使用了2个...)并将其删除。 当然,您也可以将最后两个步骤结合起来,但是如果您原来的阵列要长很多,那会比较慢。

答案 3 :(得分:0)

我首先使用itertools.permutations为元组上长度为3的置换生成一个迭代器。 然后,我通过collections.Counter计算3个元组的子列表中每个元组的所有频率,如果元素的频率超过2,则不将该子列表添加到最终列表中。

string str = "IsRecorded<code>0</code>";
str = str.Replace("<code>0</code>", "");
Console.WriteLine(str);

输出看起来像

import itertools as it
from collections import Counter

li = [(1,2),(2,3),(2,4),(2,5),(2,6),(3,1),(3,2),(3,4)]
perms = []

#Generate all permutations of length 3
for l in it.permutations(li, 3):
    flag = True
    counters = Counter()
    #Iterate through tuple and calculate frequency of each digit via Counter
    #Summing up counters for each tuple
    for tup in l:
        c = Counter(tup)
        counters += c
    #Iterate through the frequency dict, and if a value is above 2, don't add that tuple
    for v in dict(counters).values():
        if v > 2:
            flag = False
    if flag:
        perms.append(l)

print(perms)