algorithm - 将 14 人分成 4 人一组,每个人至少见面一次

标签 algorithm math combinatorics

我的一个房间里有 14 个人,房间中间有一张 table 和 4 张椅子。我希望每个人至少与其他人一起坐在 table 边一次(即,他们以 4 人为一组,与房间里的其他人一起坐在 table 边)。我想知道我需要的最小组数是多少以及如何通过算法找到这些组。

奇怪的是,这不是一个家庭作业问题,但一位家庭成员正在尝试组织一次 Zoom 通话,作为一名数学学生,我对此很感兴趣。我环顾四周,发现这篇文章尝试了类似的东西( http://zulko.github.io/blog/2013/11/08/placing-your-employees-so-that-everyone-meets/ ),并考虑了社交高尔夫球手问题( https://www.metalevel.at/sgp/#:~:text=The%20Social%20Golfer%20Problem%20(SGP,denoted%20by%20the%20triple%20g%2Dp%2Dw.) 但我仍然不确定。我得到了 23~ 一些非常杂乱的纸质涂鸦,但我现在不能破译。

最佳答案

您有 C_4_14 种可能的组合 (14*13*12*11/(4*3*2*1)) = 1001可能为 4 人一组。

您需要确保拥有所有可能的对 C_2_14 14*13/(2*1) = 91对。

每组 4 人,您有 C_2_4 = 6 对。

让我们为每个可能的对分配一个位(我们需要一个 91 位整数)。
现在,每组 4 个都可以写为 91 位整数签名,仅用 6 位集表示 6 对。

现在的问题是组装这些组,以便每对至少代表一次。
这相当于 bitOr 运算(无论该对是否在一个组中 OR 在另一个组中)。
由于我们希望所有对都被表示,因此我们希望选择可能的 1001 个组中的一个子集,使得该子集的 bitOr 为 2^91-1(所有对至少被表示一次)。

由于我们有 91 对,每组 6 对,6*15 < 91 < 6*16 ,这给了我们一个下限:我们至少需要 16 组 4 组才能实现这一点,也许更多。
最好的情况下,我们将有 5 对人会面两次,最糟糕的是,一对人会面 6 次。

为了解决这个问题,我们必须选择对的索引,例如:

(1,2) = 1
(1,3) = 2
...
(1,14) = 13
(2,3) = 14
...
(2,14) = 25
...
(13,14) = 91

这里是一个用于形成配对的 Squeak Smalltalk 代码示例

people := #(mary john clara tim julia mike vanessa bob jean harry kim donald morgan roy).
pairMap := Dictionary new: 91.
i := 0.
people combinations: 2 atATimeDo: [:p |
    pairMap at: p copy put: (i := i+1)].

现在,对于每四人一组,我们可以关联配对签名:

quadMap := Dictionary new: 1001.
iQuadMap := Dictionary new: 1001.
people combinations: 4 atATimeDo: [:q | | j |
    j := 0.
    q combinations: 2 atATimeDo: [:p| j := j bitAt: (pairMap at: p) put: 1].
    quadMap at: q copy put: j.
    iQuadMap at: j put: q copy].

一个非常简单的解决方案可能是枚举 nGroup 四边形的组合,直到找到覆盖所有对的组合。 nGroup 将从 16 开始。

allPairs := 1<<91-1.
16 to: 91 do: [:nGroup |
    quadMap values combinations: nGroup atATimeDo: [:g |
        (g reduce: #bitOr:) = allPairs ifTrue: [^g collect: [:signature | iQuadMap at: signature]]]].

正如你所看到的,组合是巨大的,C_16_1001 = 43,051,823,251,587,106,104,672,087,435,021,150所以上面的循环可能无法在合理的时间内实现,并且在这个阶段我们甚至不知道是否有16组的解决方案!

我们可以尝试一些简单的贪婪算法:选择下一组作为迄今为止解决方案签名最大化位数(最小化重叠位)的一组。
可以通过 bitAnd 运算来计算重叠位

allPairs := 1<<91-1.
signatureSoFar := 0.
groups := OrderedCollection new.
[ signatureSoFar = allPairs]
    whileFalse:
        [heap := Heap withAll: quadMap values sortBlock: [:x | (x bitAnd: signatureSoFar ) bitCount] ascending.
        groups add: heap first.
        signatureSoFar := signatureSoFar bitOr: heap first].
^groups collect: [:qSig | iQuadMap at: qSig]

运行此命令可以在几毫秒内提供 18 个组的解决方案...
使用打乱的值运行贪婪算法 10000 次并没有给出更短的解决方案(找到的解决方案在 18 到 20 组之间)。

由您决定是否可以在少于 18 个组中完成此操作。

关于algorithm - 将 14 人分成 4 人一组,每个人至少见面一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65701646/

相关文章:

c# - 反转已溢出的乘法运算

android - 在 Android 中四舍五入到小数点后两位

c++ - 使用递归将所有可能的组合(重复)作为数组中的值

algorithm - 给定一个数字大于另一个数字的 2 个数字的可能结果数

c# - 从值列表中推断出下降趋势

arrays - 如何计算二维数组中相同单元格的组数?

algorithm - 当 x < y 时,如何获得给定索引的 (x,y) 坐标?

JavaScript 数学将 Div 放置在中心(算法)

c++ - 当 vector 中唯一元素的数量远小于 vector 大小时,有效地处理 vector 的每个唯一排列

java - 当我实现快速排序方法时堆栈溢出