如何(有效地)生成不相交的集合,同时只使用一次元素对?

Posted

技术标签:

【中文标题】如何(有效地)生成不相交的集合,同时只使用一次元素对?【英文标题】:How to (efficiently) generate disjoint sets while usings pairs of elements only once? 【发布时间】:2015-12-24 11:20:56 【问题描述】:

我想做的是将一组 (n) 个项目分成大小相等的组(大小为 m 的组,并且为简单起见,假设没有剩余,即 n 可被 m 整除)。这样做多次,我想确保没有任何一对项目在同一组中出现两次

为了更具体一点,为了将六个项目中的两个组成一组A..F,一次可以以不同的方式将集合划分五次:

(A, B), (C, D), (E, F) (A, C), (B, E), (D, F) (A, D), (B, F), (C, E) (A, E), (B, D), (C, F) (A, F), (B, C), (D, E)

同一组项目只能划分一次,分成三个一组,而不会重叠:

(A, B, C), (D, E, F)

(正如@DavidHammen 在下面指出的那样,在此示例中创建分区有不同的方法。但是,一旦创建了分区,就再也没有第二个分割将所有成对的项目分开了。那就是很好——我的应用程序不需要生成所有可能的全局分区方法,一个满足约束的解决方案就可以了)


我现在的问题是:有没有办法有效地做到这一点?有没有加快生成这些集合的技巧?

到目前为止,我一直将此视为exact cover 问题,并使用backtracking algorithm(DLX 的一种变体)解决它。这对配对非常有效,但随着组变得更大,算法必须考虑的可能性数量会激增,并且处理变得非常笨拙。

我正在寻找的是加快速度的技巧。任何想法都非常受欢迎,特别是(但不限于):

优化和启发式以减少在求解之前需要考虑的可能性的数量(例如,从上面的示例中可以清楚地看出,第一次拆分可以简单地任意进行,而第一组每个分区[上面的第一列]可以自动生成)。 是否有可以应对大量候选人的回溯变体? (即不需要事先生成所有可能性) 我应该考虑的其他算法方法或数学概念?

非常欢迎任何想法和建议。 非常感谢您考虑这一点!


更新

好的,这已经有一段时间了,但我在这方面花了很多时间,想回复你。 @david-eisenstat 通过给我正确的搜索词(非常感谢!)让我走上了正确的道路——我已经阅读了很多关于社交高尔夫球手问题的文章。

我发现的最好的资源之一,我想在这里分享,是Markus Triska 的工作,他在他的论文中讨论了几种方法(然后继续提出一个非常好的算法)。如果有人遇到类似问题,强烈建议这样做!

【问题讨论】:

Re 同一组项目只能分成一次,分成三个一组而不重叠对((A B D), (C E F))((A B E) (C D F))有什么问题,至少还有六个?所述问题并未说明您排除这些组合的原因。 @DavidHammen 对 A B 似乎在两个分区中的同一组中。在 OP 的问题中,它指出 没有一对项目在同一组中出现两次 非常感谢你们两个! @DavidHammen:您完全正确,因为我可以使用您的任何示例而不是我给出的示例。我将在问题中澄清这一点。 @a-s-h:我的意图完全正确,感谢您的澄清!再次感谢您的努力和对问题的思考。 您可能需要搜索关键字:组合设计、正交数组。 【参考方案1】:

以Social Golfer Problem 的名义研究这个问题。文献规模庞大,但主要有以下三种方法:

    本地搜索方法,可以处理很多对不存在的情况。

    完整的搜索方法,例如还原到精确覆盖。据我记得,这里的研究围绕着有效的对称破坏方法展开,其中你修复第一行的想法可能是最简单的。

    数学结构。当 q 是主要幂时,有一个涉及finite affine planes 的 q 组 q 的构造,实现起来并不太糟糕。除此之外,还有很多一次性的结构。组合设计手册可能是您总结已知知识的最佳选择。

【讨论】:

哇,非常感谢您快速而全面的回答!您的指针在查找其他文献和实现指针方面非常有帮助。我也看过《组合设计手册》,看起来我只需要深入研究数学:-)。我现在已经对您的答案投了赞成票(希望我可以多次这样做),当我阅读完一些内容后,我会回来并将我的发现添加为评论。【参考方案2】:

n=m*k,分区有m 组和k 项。

x 分区之后,每个项目都与x*(k-1) 其他项目在一个组中。创建t-1分区后,在下一个分区A可以选择:

second element : n -   (t-1)*(k-1)   - 1  items
third element  : n - 2*(t-1)*(k-1)   - 2  items
fourth element : n - 3*(t-1)*(k-1)   - 3  items
...
k'th element   : n -   (t-1)*(k-1)^2 - (k-1)  items

要创建t'th 分区,我们需要:

n - (t-1)*(k-1)^2 - (k-1) > 0
=>
t < (n - k + 1) / ((k-1)^2) + 1

可能的分区数随着组长度的平方而减少。这意味着,没有太多可能的分区:-)

我会采用一些贪婪的方法。为每个项目存储可用项目集,并通过将第一个可用项目添加到组来创建新分区。

【讨论】:

以上是关于如何(有效地)生成不相交的集合,同时只使用一次元素对?的主要内容,如果未能解决你的问题,请参考以下文章

并查集(不相交集)的Union操作

如何有效地生成组合而不重复,它们之间有特定的数字

如何在 C++ 中更有效地只生成这么多排列?

有效地测试元素是不是会在 jQuery 集合中

Python入门教程第51篇 不相交集

三路集合不相交(Three-Way Set Disjointness)算法分析