【问题标题】:How to generate all the subsets of a counter?如何生成计数器的所有子集?
【发布时间】:2020-07-14 02:49:59
【问题描述】:

我需要编写一个名为char_counts_subsets 的函数,该函数将字符计数字典作为参数,并在考虑字符计数值的情况下返回该字典的所有子集。示例代码如下所示:

char_counts = {"a": 1, "b": 2}

def char_counts_subsets(cc):
    return [{},
            {"b": 1}, {"b": 2}, {"a": 1},
            {"a": 1, "b": 1}, {"a": 1, "b": 2}
            ] # ordering of the subsets isn't important

print(char_counts_subsets(char_counts))

我怎样才能概括这个函数,以便它可以与任何cc 字典一起使用?

【问题讨论】:

    标签: python python-3.x dictionary itertools


    【解决方案1】:

    这是一个组合问题,最好使用itertools 解决。

    from itertools import product
    

    将每个字典项展开为一系列项:

    range_items = [[(x, z) for z in range(y + 1)] for x,y in char_counts.items()]
    #[[('a', 0), ('a', 1)], [('b', 0), ('b', 1), ('b', 2)]]
    

    对每个范围内的每个项目与所有其他范围内的每个项目进行笛卡尔积:

    products = product(*range_items)
    #[(('a', 0), ('b', 0)), (('a', 0), ('b', 1)),...(('a', 1), ('b', 2))]
    

    消除具有 0 个计数器的对,并将剩余部分转换为具有 dict 理解的字典:

    [{k: v for k, v in pairs if v > 0} for pairs in products]
    #[{}, {'b': 1}, {'b': 2}, {'a': 1}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}]
    

    【讨论】:

      【解决方案2】:

      我喜欢DYZ's answer,但我想知道是否有可能使它成为一个高效的迭代器。 DYZ 的range_items 的空间复杂度类似于 O(n+m),其中 n 是元素的数量,m 是它们的计数之和。我的解决方案在ranges 本身上使用product,我很确定这是 O(n)。

      另外,对于术语,char_counts 基本上是一个multiset,输出与power set 非常相似,所以我猜你会称它为“power multiset”。顺便说一句,看看collections.Counter,它是标准库中的一个多集对象。

      import itertools
      
      def power_multiset(multiset):
          """
          Generate all sub-multisets of a given multiset, like a powerset.
      
          Output is an iterator of dicts.
          """
          elems = []
          ranges = []
          for elem, count in sorted(multiset.items()):
              elems.append(elem)
              ranges.append(range(count+1))
      
          for sub_counts in itertools.product(*ranges):
              # "if c" filters out items with a 0 count
              yield {e: c for e, c in zip(elems, sub_counts) if c}
      
      >>> char_counts = {"a": 1, "b": 2}
      >>> list(power_multiset(char_counts))
      [{}, {'b': 1}, {'b': 2}, {'a': 1}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}]
      

      【讨论】:

      • 另一方面,如果range_items 的大小实际上是一个问题,那么输出 的大小无论如何都会使整个任务完全没有希望。此外,range_items 的空间消耗仅与计数的总和成正比,而不是乘积。
      • @user2357112 感谢您的反馈。我编辑澄清。我想象一个一个地遍历子多重集,在这种情况下,迭代器更有效。我还修复了 big-O;是现在吗?
      【解决方案3】:

      没有 itertools,这对我有用。可能需要稍微缩短一下,以便更好地获取密钥。这是我无需查找任何内容的最快方法。

      def char_counts_subsets(cc):
          subset = []
          for key in cc:
              subset.append({key: cc[key]})
              if cc[key]!= 1:
                  for i in range(1, cc[key]):
                      subset.append({key: i})
          subset2 = []
          for i, item in enumerate(subset):
              for key in item:
                  newitem = {key: item[key]}
                  for item2 in subset:
                      for key2 in item2:
                          if key != key2:
                              newitem.update({key2: item2[key2]})
                              if newitem not in subset2:
                                  subset2.append(newitem)             
          subset.extend(subset2)
          return subset
      

      【讨论】:

      • 我怀疑您的解决方案取决于输入:由于我的示例字典长度为 2,因此您可以使用 4 个嵌套循环来解决问题。 for item in subset: for key in item: for item2 in subset: for key2 in item2: 但是,如果您向字典中添加另一个键,它将无法按预期工作。
      • 我运行了这个,它对我有用:char_counts = {"a": 1, "b": 2, "c": 3}
      • 顺序并不完美,但确实给出了所有子集
      • 这里是结果 [{'a': 1}, {'b': 2}, {'b': 1}, {'c': 3}, {'c': 1}, {'c': 2}, {'a': 1, 'b': 1, 'c': 2}, {'b': 2, 'a': 1, 'c': 2} , {'b': 1, 'a': 1, 'c': 2}, {'c': 3, 'a': 1, 'b': 1}, {'c': 1, 'a ': 1, 'b': 1}, {'c': 2, 'a': 1, 'b': 1}]
      • {"a": 1, "b": 2, "c": 3} 的结果中缺少这些:[{}, {'b': 1, 'c': 1}, {'b': 1, 'c': 2}, {'b': 1, 'c': 3}, {'b': 2, 'c': 1}, {'b': 2, 'c': 2}, {'b': 2, 'c': 3}, {'a': 1, 'c': 1}, {'a': 1, 'c': 2}, {'a': 1, 'c': 3}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2, 'c': 1}, {'a': 1, 'b': 2, 'c': 3}]
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-12
      • 1970-01-01
      • 2019-04-13
      • 1970-01-01
      相关资源
      最近更新 更多