如何(有效地)生成不相交的集合,同时只使用一次元素对?
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
可能的分区数随着组长度的平方而减少。这意味着,没有太多可能的分区:-)
我会采用一些贪婪的方法。为每个项目存储可用项目集,并通过将第一个可用项目添加到组来创建新分区。
【讨论】:
以上是关于如何(有效地)生成不相交的集合,同时只使用一次元素对?的主要内容,如果未能解决你的问题,请参考以下文章