【问题标题】:How to get the maximum amount of tuples that are not contained in another set yet?如何获得最大数量的未包含在另一个集合中的元组?
【发布时间】:2022-01-20 16:28:00
【问题描述】:

给定一个字母列表,例如 L=['a','b','c','d','e','f'] 和一个元组列表,例如 T=[(' a','b'),('a','c'),('b','c')].

现在我想从 L 的列表中创建最大数量的未包含在 T 中的元组。这需要在没有重复的情况下完成,即 (a,b) 将与 (b,a) 相同。此外,每个字母只能与另一个字母匹配。

我的想法是:

#create a List of all possible tuples first:

all_tuples = [(x,y) for x in L for y in L if x!=y]

#now remove duplicates

unique_tuples = list(set([tuple(sorted(elem)) for elem in all_tuples]))

#Now, create a new set that matches each letter only once with another letter

visited=set()
output = []

for letter1, letter2 in unique tuples:
   if ((letter1, letter2) or (letter2, letter1)) in T:
      continue
   
   if not letter1 in visited and not letter2 in visited:
      visited.add(letter1)
      visited.add(letter2)
      output.append((letter1,letter2))

print(output)

然而,这并不总是给出可能的元组的最大数量,这取决于 T 是什么。例如,假设我们提取可能的 unique_tuples=[('a','b'),('a','d'),('b','c')]。

如果我们先将 ('a','b') 附加到我们的输出中,我们将无法再附加 ('b','c'),因为 'b' 已经匹配。但是,如果我们先附加 ('a','d'),我们也可以在之后得到 ('b','c') 并获得最大数量的两个元组。

如何解决这个问题?

【问题讨论】:

  • 我在遵循这个例子时遇到了麻烦——你不能有 ('b', 'c') 因为它已经在 T 中,对吧?鉴于 T 重复使用字母,“与另一个字母匹配”如何工作?
  • 对不起,我的写作令人困惑。我的意思是从集合 [('a','b'),('a','d'),('b','c')] 中最多可以提取两个元组,使得每个字母都匹配最多一次。但是,如果先选择 ('a','b'),则无法选择其他元组。
  • 而集合 T 是一个给定的集合,不需要满足字母只能匹配一次的规则。该规则仅适用于输出。

标签: python matching


【解决方案1】:

如果我们忽略两次不匹配同一个字母的业务,这是combinations的直接用法:

>>> from itertools import combinations
>>> L=['a','b','c','d','e','f']
>>> T=[('a','b'),('a','c'),('b','c')]
>>> [t for t in combinations(L, 2) if t not in T]
[('a', 'd'), ('a', 'e'), ('a', 'f'), ('b', 'd'), ('b', 'e'), ('b', 'f'), ('c', 'd'), ('c', 'e'), ('c', 'f'), ('d', 'e'), ('d', 'f'), ('e', 'f')]

如果我们限制自己只使用每个字母一次,问题就很简单了,因为我们知道我们最多只能有 (letters / 2) 个元组。只需找到可用的字母(通过减去 T 中已经存在的字母),然后以任意顺序将它们配对。

>>> used_letters = {c for t in T for c in t}
>>> free_letters = [c for c in L if c not in used_letters]
>>> [tuple(free_letters[i:i+2]) for i in range(0, 2 * (len(free_letters) // 2), 2)]
[('d', 'e')]

【讨论】:

  • 谢谢,以后试试!
  • 这不是我的意思。我们仍然可以创建不包含在 T 中的元组,例如 (a,e)。
  • 您现在所做的是创建了一个元组列表,其中不包含任何出现在 T 中的字母。
【解决方案2】:

不使用库,你可以这样做:

L=['a','b','c','d','e','f']
T=[('a','b'),('a','c'),('b','c')]

L = sorted(L,key=lambda c: -sum(c in t for t in T))
used = set()
r = [(a,b) for i,a in enumerate(L) for b in L[i+1:]
     if (a,b) not in T and (b,a) not in T
     and used.isdisjoint((a,b)) and not used.update((a,b))]

print(r)
[('a', 'd'), ('b', 'e'), ('c', 'f')]

字母在组合之前按 T 中的频率降序排列。这可确保首先处理最难匹配的字母,从而最大限度地提高剩余字母的配对潜力。

或者,您可以使用递归 (DP) 方法并检查所有可能的配对组合。

def maxTuples(L,T):
    maxCombos = []                          # will return longest
    for i,a in enumerate(L):                # first letter of tuple
        for j,b in enumerate(L[i+1:],i+1):  # second letter of tuple
            if (a,b) in T: continue         # tuple not in T
            if (b,a) in T: continue         # inverted tuple not in T
            rest = L[:i]+L[i+1:j]+L[j+1:]   # recurse with rest of letters
            R = [(a,b)]+maxTuples(rest,T)   # adding to selected pair
            if len(R)*2+1>=len(L): return R # max possible, stop here
            if len(R)>len(maxCombos):       # longer combination of tuples
                maxCombos = R               # Track it
    return maxCombos

...

L=['a','b','c','d','e','f']
T=[('a','b'),('a','c'),('b','c'),('c','f')]

print(maxTuples(L,T))        

[('a', 'd'), ('b', 'f'), ('c', 'e')]


L = list("ABCDEFGHIJKLMNOP")
T = [('K', 'N'), ('G', 'F'), ('I', 'P'), ('C', 'A'), ('O', 'M'), 
     ('D', 'B'), ('L', 'J'), ('E', 'H'), ('F', 'E'), ('L', 'H'), 
     ('J', 'G'), ('N', 'I'), ('C', 'M'), ('A', 'P'), ('D', 'O'), 
     ('K', 'B'), ('G', 'H'), ('O', 'A'), ('I', 'J'), ('N', 'M'), 
     ('F', 'P'), ('E', 'B'), ('K', 'L'), ('D', 'C'), ('D', 'E'), 
     ('L', 'F'), ('B', 'H'), ('I', 'A'), ('K', 'G'), ('M', 'O'), 
     ('P', 'C'), ('N', 'J'), ('J', 'E'), ('N', 'P'), ('A', 'G'), 
     ('H', 'O'), ('I', 'B'), ('K', 'F'), ('M', 'C'), ('L', 'D'), 
     ('A', 'B'), ('C', 'E'), ('D', 'F'), ('G', 'I'), ('H', 'J'), 
     ('K', 'M'), ('L', 'N'), ('O', 'P')]

print(maxTuples(L,T))        

[('A', 'D'), ('B', 'C'), ('E', 'G'), ('F', 'H'), 
 ('I', 'K'), ('J', 'M'), ('L', 'P'), ('N', 'O')]

请注意,如果 T 中的元组排除了太多配对以致无法生成 len(L)/2 个元组的组合,则该函数会很慢。它可以通过过滤在递归过程中完全排除的字母来进一步优化:

def maxTuples(L,T):
    if not isinstance(T,dict):        
        T,E = {c:{c} for c in L},T               # convert T to a dictionary
        for a,b in E: T[a].add(b);T[b].add(a)    # of excluded letter sets
    L = [c for c in L if not T[c].issuperset(L)] # filter fully excluded   
    maxCombos = []                          # will return longest
    for i,a in enumerate(L):                # first letter of tuple
        for j,b in enumerate(L[i+1:],i+1):  # second letter of tuple
            if b in T[a]: continue          # exclude tuples in T
            rest = L[:i]+L[i+1:j]+L[j+1:]   # recurse with rest of letters
            R = [(a,b)]+maxTuples(rest,T)   # adding to selected pair
            if len(R)*2+1>=len(L): return R # max possible, stop here
            if len(R)>len(maxCombos):       # longer combination of tuples
                maxCombos = R               # Track it
    return maxCombos

【讨论】:

  • 谢谢!我也有使用图书馆 collections.Counter 的方法。但是,正如您所说,它只会增加可能性,并不能保证元组的最大数量。
  • 不过,“and not used.update((a,b))”到底是做什么的?谢谢
  • and not used.update((a,b))ab 中的字母添加到used 集合中,这样这些字母就不会再次用于剩余的元组。
  • 明天我会尝试用我的方法对我不起作用的数据,然后告诉你!
  • 它不能正常工作。举个例子:
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2012-02-15
  • 1970-01-01
  • 1970-01-01
  • 2019-10-22
  • 2014-12-18
  • 2012-02-26
相关资源
最近更新 更多