【问题标题】:How to get all combinations with no repeats?如何获得没有重复的所有组合?
【发布时间】:2017-11-19 19:06:06
【问题描述】:

所以,我从这个数组开始:

array = ['A', 'B', 'C', 'D', 'E', 'F']

我玩了一会儿,然后让 python 打印每个独特的、不重复的组合,如下所示:

AB,
AC,
AD,
AE,
AF,
BC,
BD,
BE,
BF,
CD,
CE,
CF,
DE,
DF,
EF,

但是现在,我想将所有这些都放入一个新数组中:

array2 = ['AB', 'AC', 'AD'...., 'EF']

并打印所有 3 元素长的组合,不包括重新排列,没有重复。

我所说的“不重复”是什么意思:

ABCDEF 是没有重复的 3 元素长组合,但 ABBDEF 是带有重复的 3 元素长组合,如 @ 987654331@ 出现在“AB”和“BD'”中。

我所说的“不包括重新排列”是什么意思:

AB、CD、EF 与 BA、DC、FE 相同,因为所有 2 字母元素都是相同的(BA 是 AB 重排,DC 是 CD 重排,FE 是 EF 重排)。所以理想情况下它会打印如下内容:

AB CD EF,
AB CE DF,
AB CF DE,
AC BD EF,
AC BE DF,
AC BF DE,
AD BC EF,
AD BE CF,
AD BF CE,
AE BC DF,
AE BD CF,
AE BF CD,
AF BC DE,
AF BD CE,
AF BE CD,

我相信这些都是没有重复 2 字母元素的所有组合。

我将如何打印这个?谢谢!

【问题讨论】:

  • 你在尝试解决一个练习吗?
  • 所以你想保持第一个位置,即A,固定并排列其余位置?或者BA CD EF 也应该有效?
  • 6 个元素的所有组合(有排列)有什么区别?
  • 您使用什么代码来创建 2 项组合列表?
  • 我想指出,有itertools.combinations 可以获得每个独特的、不重复的组合。

标签: python arrays combinations


【解决方案1】:

Generator 基于递归方法(没有任何迭代工具):

def comb(s):
  if len(s) == 2:
    yield [s]
  for x in s[1:]:
    first = ''.join((s[0], x))
    rest = ''.join(c for c in s if c not in first)
    for com in comb(rest):
      yield [first] + com

>>> list(comb('ABCDEF'))
[['AB', 'CD', 'EF'],
 ['AB', 'CE', 'DF'],
 ['AB', 'CF', 'DE'],
 ['AC', 'BD', 'EF'],
 ['AC', 'BE', 'DF'],
 ['AC', 'BF', 'DE'],
 ['AD', 'BC', 'EF'],
 ['AD', 'BE', 'CF'],
 ['AD', 'BF', 'CE'],
 ['AE', 'BC', 'DF'],
 ['AE', 'BD', 'CF'],
 ['AE', 'BF', 'CD'],
 ['AF', 'BC', 'DE'],
 ['AF', 'BD', 'CE'],
 ['AF', 'BE', 'CD']]

这将获取第一个元素,将其与其他每个元素配对,并将结果对与可以从剩余元素组成的每个详尽的配对列表组合起来。基本情况是只有两个元素。

注意:rest 的组合前提是初始字符串/序列中没有重复。

【讨论】:

  • 我确实喜欢递归生成器。 ;)
  • 查看我的答案,了解您的算法与蛮力 itertools.combinations 解决方案之间的速度比较。我相信您不会感到惊讶。 :)
  • 您是否有机会获得所有包含大约 100 个唯一变量的组合?也许通过将 100 个变量分成 10 个块,然后重新组合它们?
【解决方案2】:

这是一个使用itertools 的蛮力非递归版本。它生成对,然后从这些对中生成所有组合,使用集合来消除重复任何字母的组合。它比 schwobaseggl 的生成器效率低很多,但它对于小字符串仍然相当快,因为​​ combinations 非常快。

from itertools import combinations

def pairs(s):
    n = len(s)
    numgroups = n // 2
    for v in combinations(map(''.join, combinations(s, 2)), numgroups):
        if len(set(i for u in v for i in u)) == n:
            yield v

for t in pairs('ABCDEF'):
    print(t)

输出

('AB', 'CD', 'EF')
('AB', 'CE', 'DF')
('AB', 'CF', 'DE')
('AC', 'BD', 'EF')
('AC', 'BE', 'DF')
('AC', 'BF', 'DE')
('AD', 'BC', 'EF')
('AD', 'BE', 'CF')
('AD', 'BF', 'CE')
('AE', 'BC', 'DF')
('AE', 'BD', 'CF')
('AE', 'BF', 'CD')
('AF', 'BC', 'DE')
('AF', 'BD', 'CE')
('AF', 'BE', 'CD')

在我的 2GHz 机器上,打印 pairs('ABCDEFGHIJ') 的 945 结果大约需要 13 秒。相比之下,schwobaseggl 的comb 只需要 0.193 秒。 :)


这是一个更智能的 itertools 版本。这个仍然比必要的工作多,但它只比 schwobaseggl 的 comb 生成器慢两倍。我们首先产生原始字符串一半大小的组合,然后使用set.difference 产生互补组合。然后我们置换该组合以将其与原始组合组合。

from itertools import combinations, permutations

def pairs(s):
    a = set(s)
    for u in combinations(s, len(s) // 2):
        b = tuple(sorted(a.difference(u)))
        if b < u:
            break
        for v in permutations(b):
            c = [*zip(u, v)]
            if all(i<j for i, j in c):
                yield [*map(''.join, c)]

【讨论】:

  • 大声笑,最后一次尝试看起来像是关键字和函数工厂的大师级爆炸!有一个费率:D
  • @schwobaseggl 谢谢。很难为此想出不仅仅是伪装版本的算法。 :)
  • 是的,这是一个相当不错的学术练习......而且根本不是标记问题的重复。我也可以陶醉于那些年代。
【解决方案3】:

PM 2Ring 代码的更快变体:

from itertools import combinations
array = ['A', 'B', 'C', 'D', 'E', 'F']
n = len(array)
numgroups = n // 2

array2 = map(''.join, combinations(array,2))
result = (' '.join(com) for com in combinations(array2,numgroups) if len(set(''.join(com)))==n)
for res in result:
    print res

【讨论】:

    猜你喜欢
    • 2022-12-21
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多