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

时间:2015-09-26 21:38:52

标签: algorithm combinatorics solver constraint-programming disjoint-sets

我想要做的是将一组( 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的工作,他在论文中讨论了几种方法(然后继续提出一个非常好的算法)。如果有人遇到类似的问题,强烈建议这样做!

2 个答案:

答案 0 :(得分:8)

此问题的研究名称为Social Golfer Problem。文献具有重要的规模,但有三种主要方法:

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

  2. 完整的搜索方法,例如缩小到完全封面。根据我的记忆,这里的研究围绕着对称破坏的有效方法,你修复第一行的想法可能是最简单的。

  3. 数学结构。当q是一个主要的力量时,q个q组的构造涉及finite affine planes并不太难以实现。除此之外,还有很多一次性的结构。 “组合设计手册”可能是您总结已知内容的最佳选择。

答案 1 :(得分:0)

允许n=m*k,分区包含mk项组。

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

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

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