【问题标题】:Combinations of a group into multiple groups without repetition将一个组组合成多个组而不重复
【发布时间】:2019-04-19 09:21:56
【问题描述】:

我们有一个元素列表[A,B,C,D,E]3组来组合这些元素,我想打印一个列表所有这 3 组没有重复的独特组合。只有长度组 [2,2,1] 是有效的(它可以在之后被过滤,这不是必须的,但它会很有效,并且如果需要,可以手动指定组的长度作为参数)。我的意思是独特而不重复:

[[A,B],[C,D],[E]] 和 [[C,D],[A,B],[E]] 和 [[B,A],[C,D ],[E]] 将是相同的,因此组内元素的顺序或组的顺序无关紧要,我对这些组合不感兴趣。

在我的特定情况下,我有 16 个项目和 3 组,每组 5、5 和 6 个项目。

我想要实现的一个例子:

/**
 * Returns all the unique combinations of a group into multiple groups
 * [data] the group of elements to combine
 * [numberOfGroups] the number of groups
 */
fun combinations(data: List<String>, numberOfGroups: Int): List<List<List<String>>> {
    // The combinations code
}

val data = listOf("A", "B", "C", "D", "E")
print(combinations(data, 3))

输出将是这样的:

[
   [[A,B],[C,D],[E]],
   [[A,D],[B,C],[E]],
   ...
]

提前谢谢你。

【问题讨论】:

  • 到目前为止你尝试过什么?你被困在哪里了?
  • @Nikolas 首先我尝试自己提出解决方案,要么太复杂,要么我不够聪明(我也没有数学背景)。我发现了 n! / (r!(n−r)!) 组合的公式,但我认为它不适用于这里,我尝试了 Guava 库中的 Sets.powerSet(Sets.newHashSet(myItemsList))。我也花了几个小时寻找示例,但没有一个适用。

标签: java kotlin unique combinations


【解决方案1】:

我一般不知道您的问题的答案,但我会尝试解决将 5 个元素的列表拆分为组 [2, 2, 1] 的特殊情况,并分享一些可能有所帮助的原则您可以设计一个更通用的解决方案。

首先,让我们谈谈如何表示结果。如果组内元素的顺序不重要,可以方便地用Set&lt;String&gt; 表示组,这样setOf("A", "B") 等于setOf("B", "A")。然后,如果组合中组本身的顺序无关紧要,则该组合可以是一组组,即Set&lt;Set&lt;String&gt;&gt;

现在谈谈算法本身。将这种算法构造为递归搜索很方便:您从数据中选择第一组项目,然后为没有选定项目的数据解决问题,并将第一组与除它之外的所有解决方案结合起来。所以我们搜索组合的函数可以是这样的:

fun combinationSequence(data: List<String>): Sequence<Set<Set<String>>> = sequence {
    for (group in possibleFirstGroups(data)) {
        val remaining = data - group
        if (remaining.isEmpty()) {
            yield(setOf(group))
        } else {
            yieldAll(combinationSequence(remaining).map { r -> setOf(group) + r })
        }
    }
}

然后如何以所有可能的方式选择第一组。对于大小为 1 或 2 的组,我们可以从所有数据元素中选择组的第一个元素,然后从剩余的元素中选择第二个元素:

fun possibleFirstGroups(data: List<String>): Sequence<Set<String>> =
        when (data.size) {
            0 -> emptySequence()
            1, 2 -> sequenceOf(data.toSet())
            else -> sequence {
                for (e1 in data) {
                    for (e2 in data - e1) {
                        yield(setOf(e1, e2))
                    }
                }
            }
        }

我们的combinationSequence返回结果,但是会有很多重复,比如[[A, B], [C, D], [E]][[C, D], [A, B], [E]]。我们可以使用函数distinct

combinationSequence(data).distinct().forEach { println(it) }

请注意,这个解决方案的复杂性随着项目的数量呈指数增长,所以我不希望 16 个元素的解决方案及时终止 :)

降低复杂性的一种方法是修剪搜索空间。例如,如果我们已经找到以[A, B] 组开头的所有 组合,我们可以避免产生包含该组在中间某处的组合,例如[C, D], [A, B], ...

【讨论】:

    猜你喜欢
    • 2017-03-20
    • 2018-11-14
    • 2013-02-06
    • 2012-05-20
    • 1970-01-01
    • 2016-07-28
    • 2019-03-22
    • 1970-01-01
    • 2022-06-15
    相关资源
    最近更新 更多