【问题标题】:Algorithm for finding the smallest lists that together make up the target list [closed]查找共同构成目标列表的最小列表的算法[关闭]
【发布时间】:2021-03-19 19:44:52
【问题描述】:

假设我有以下内容:

{
  'a': [1, 2, 3],
  'b': [1, 5],
  'c': [3, 4, 5],
  'd': [1, 3, 5],
  'e': [4]
}

想要的结果是['a', 'c'] 因为我想找到合并在一起的数组(并删除重复项)表单[1 , 2, 3, 4, 5]

除了合并在一起形成所需结果的数组之外,我还想找到要合并的最小数组以获得所需结果(因为例如 ['a', 'd', 'e'] 也给出了所需结果,但 ['a', 'c'] 是更好的解决方案)

PS。上面的字典只是一个例子,原来的字典有很多键,每个键都有数百个值。

【问题讨论】:

    标签: python list merge


    【解决方案1】:

    沿着这条线的东西应该可以工作。您需要根据您是否希望对其进行排序等进行一些调整。此解决方案假定顺序无关紧要。

    从最小到最大打印解决方案:

    import itertools
    
    input = {
      'a': [1, 2, 3],
      'b': [1, 5],
      'c': [3, 4, 5],
      'd': [1, 3, 5],
      'e': [4]
    }
    
    solution = [1,2,3,4,5]
    for i in range(1,len(input.keys())):
        for combination in itertools.combinations(input, i):
            pot = list(set(itertools.chain.from_iterable(input[k] for k in combination)))
            if pot == solution:
                print("This is a solution:", combination)
    

    【讨论】:

    • 谢谢,我猜 itertools 是我需要的工具。
    【解决方案2】:

    这可能不是很优雅,但它会比暴力破解所有组合更有效。它应该在二次时间内执行,而不是根据条目数量和数据传播而变成指数的组合。

    以下函数可以找到一个简短的解决方案。很可能(但不一定)最短。

    from collections import Counter
    
    def findMerge(data,target):
        # identify candidate items (i.e. subsets of the target list)
        target     = set(target)
        candidates = {c:group for c,group in data.items() if target.issuperset(group)}
        
        # compute the overlap between candidates and check coverage
        counts = Counter( n for group in candidates.values() for n in group )
        if any(t not in counts for t in target): return [] 
    
        # identify candidates that are mandatory and the base set they form
        # (i.e. candidates that are the only ones with a given value)
        mandatory = { c:group for c,group in candidates.items()
                      if any(counts[n]==1 for n in group) }
        baseSet   = set().union(*mandatory.values())
        remaining = target - baseSet
        if not remaining: return list(mandatory)
       
        # identify potentially redundant candidates for remaining values
        redundant = [ (c,remaining.intersection(group))
                      for c,group in candidates.items() if c not in mandatory ]
    
        # remove redundant candidates (smallest first)
        # note: using combinations only on redundant keys may be affordable here
        #       and could be used to return all solutions or ensure shortest
        redundant = sorted(redundant,key=lambda cg:len(cg[1]))
        for r,rGroup in redundant:
            if all(counts[n]>1 for n in rGroup):
                counts.subtract(rGroup)
                del candidates[r]
            
        return list(candidates)
    

    小样本的输出:

    data = {
      'a': [1, 2, 3],
      'b': [1, 5],
      'c': [3, 4, 5],
      'd': [1, 3, 5],
      'e': [4]
    }
    
    print(findMerge(data,[1,2,3,4,5])) # ['a', 'c']
    

    对于更大的样本,与组合相比的时间差异会很大:

    data = {
      'a': [1, 2, 3],
      'b': [1, 5, 0],
      'c': [3, 2, 5],
      'd': [1, 3, 5],
      'e': [4],
      'f': [1, 2, 5],
      'g': [1, 5, 8],
      'h': [3, 4, 7],
      'i': [1, 6, 5],
      'j': [4],
      'k': [9],
      'l': [1, 3, 5],
      'm': [4],
      'n': [1, 2, 5],
      'o': [1, 5, 8],
      'p': [3, 4, 7],
      'q': [1, 5, 8],
      'r': [3, 4, 7],
      's': [1, 6, 5],
      't': [4],
      'u': [9],
    }
    
    target = [0,1,2,3,4,5,6,7,8,9]
    print(findMerge(data,target)) # ['b', 'c', 'q', 'r', 's', 'u']
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-29
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      相关资源
      最近更新 更多