分桶问题变体的最佳方法

Posted

技术标签:

【中文标题】分桶问题变体的最佳方法【英文标题】:Best approach to a variation of a bucketing problem 【发布时间】:2020-01-14 17:11:41 【问题描述】:

在可能的日子里找到最合适的团队组成。一组 n 个参与者,k 天,一个团队有 m 插槽。参与者指定他想参加多少天以及他有空的日子。

结果约束:

    参与者参加的天数不得超过他们想要的时间 不得将参与者安排在他们不在的日子里。 算法应尽最大努力包含尽可能多的独特参与者。 如果当天可用的参与者少于 m 个,则不会安排一天。

我发现自己每周在工作中为我的足球队调度手动解决这个问题,我确信有一种智能的程序化方法可以解决这个问题。目前,我们考虑每周只有 2 天,同事写下他们想参加的那一天的姓名,最终每天都有大量的清单,不可能取悦所有人。

我考虑了一种新方法,每个同事写下他的名字、每周希望玩的时间以及他有空的日子,示例如下:

Kane 3 1 2 3 4 5

上面的线表示凯恩这周想打 3 次,他周一到周五都有空。第一个数字代表可以玩的天数,接下来的数字代表可用天数(1 到 7,周一到周日)。

参与者少于 m(在我的情况下,m = 12)的日子不会预定。解决这个问题的最佳方法是什么,才能找到一个解决方案,尽最大努力让每个参与者至少参与一次,并考虑他们的愿望(什么时候玩,玩多少)。

我会编程,我只需要知道要实现什么样的算法,并且可能对选择有一个简短的逻辑解释。

结果约束:

    参与者不得超出自己的意愿玩游戏 不得将参与者安排在他们不想参加比赛的日子里 算法应尽最大努力包含尽可能多的参与者。 如果当天可用的参与者少于 m 个,则不会安排一天。

【问题讨论】:

可以在同一天安排15个(超过m)吗? 您已经描述了集合覆盖问题的多维版本。我看到了对此的各种直接攻击——我希望看到的不是对算法的请求,而是有问题结果的部分解决方案。 暴力破解可用的法律选择应该仍然足够快,可以为您提供人类方面的快速解决方案。您在参与者数量和游戏天数之间做出了怎样的权衡?一天最多可以安排多少玩家? 在我的真实案例中,我想在一天内寻找 12 名参与者,但当天仍然可以安排只有 10 人。超过 12 人意味着有人坐在板凳上。 再一次,在我的例子中,我们使用了全部 7 天,并且有大约 40 个可能的参与者。大多数人避开周末,我们最终在前 5 天吵架。最大玩家数量仍然是m 【参考方案1】:

调度问题可能会变得非常棘手,但实际上你的问题并不算太糟糕。 (嗯,至少在你推出第一个自动计划并且人们抱怨它并且你开始添加边约束之前。)

一天可以匹配或不匹配的事实造成了使这些问题变得困难的那种非凸性,但是如果 k 很小(例如,k = 7),很容易通过所有的暴力破解2k 天匹配的可能性。对于这个答案的其余部分,假设我们知道。

弄清楚如何将人员分配到特定的匹配项可以表述为最小成本流通问题。我将把它写成一个整数程序,因为在我看来它更容易理解,而且一旦你添加了边约束,你很可能会得到一个整数程序求解器。

设 P 是人的集合,M 是匹配的集合。对于 P 中的 p 和 M 中的 m,如果 p 愿意在 m 中玩,则让 p ~ m。令 U(p) 为 p 匹配次数的上限。令 D 为每场比赛需要的人数。

对于每个 p ~ m,令 x(p, m) 是一个 0-1 变量,如果 p 在 m 中播放,则为 1,如果 p 在 m 中不播放,则为 0。对于 P 中的所有 p,令 y(p) 为 0-1 变量(直观地说,如果 p 至少参加一场比赛,则为 1,如果 p 没有比赛,则为 0,但稍等片刻)。我们有限制

# player doesn't play in too many matches
for all p in P, sum_m in M | p ~ m x(p, m) ≤ U(p)

# match has the right number of players
for all m in M, sum_p in P | p ~ m x(p, m) = D

# y(p) = 1 only if p plays in at least one match
for all p in P, y(p) ≤ sum_m in M | p ~ m x(p, m)

目标是最大化

sum_p in P y(p)

请注意,如果玩家 p 至少参加了一场比赛,我们实际上不会强制 y(p) 为 1。最大化目标为我们解决了这个问题。

您可以编写代码以编程方式将给定实例表示为mixed-integer program (MIP),就像这样。使用 MIP 公式,边际约束的限制是无限的,例如,避免连续几天与某些人比赛,将结果偏向于将至少两场比赛奖励给尽可能多的人,因为尽可能多的人获得了他们的第一场比赛,等等.等

【讨论】:

【参考方案2】:

如果您需要一个可以通过小步骤优化和完善的基本解决方案,我有一个想法。我说的是Flow Networks。大多数已经知道自己在做什么的人可能会不屑一顾,因为流网络通常用于解决最大化问题,而不是优化问题。从某种意义上说,他们是对的,但我认为最初可以将其视为每天玩游戏的玩家数量最大化。如果我们停在这里,不用说这是一种贪婪的方法。 不多介绍了,目的就是求这个图里面的最大流量:

每个玩家都有他想玩的天数,表示为从Source 到节点player x 的每条边的容量。每个玩家节点从player xday_of_week 的边数与先前找到的容量一样多。每个第 2 级边的容量为 1。第 3 级由将 day_of_week 链接到 sink 节点的边填充。快速示例:player 2 可用 2 天:周一和周二,都有玩家限制,即 12。

到目前为止,第 1、第 2 和第 4 个约束都已满足(嗯,这也是很容易的部分):在找到整个图形的最大流量后,您只需选择那些在第 2 级都没有任何剩余容量的路径(从players 到day_of_weeks)和第三级(从day_of_weeks 到sink)。很容易证明,在这种“优化”级别下,在某些条件下,它可能找不到任何可接受的路径,即使如果它在访问图形时做出不同的选择,它也会找到一条可接受的路径。

这部分就是我之前说的优化问题。我提出了至少两个启发式改进:

当您访问图表时,将day_of_weeks 存储在优先级队列中,其中分配更多玩家的日子也有更高的优先级。这样一来,整个图的剩余容量量肯定分布不均。 随机性是你的朋友。您不必只运行一次该算法,每次运行时都应该从player 级别的节点中选择一条随机边。最后,您平均结果并选择最常见的结果。这是多数规则完全适用的情况。

最好指定以上所有内容都只是一个起点:启发式的目的是找到可能的最佳近似解。对于这类问题,考虑到您的意见可能很小,这不是正确的方法,但当您不知道从哪里开始时,这是最简单的方法。

【讨论】:

以上是关于分桶问题变体的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

架构师之路Learn Day7之Hive生产环境最佳实践

分组/分桶纬度和经度

实现存储库模式的最佳方法?

检查大写/小写查询字符串的最佳方法

具有多种变体的 Foreach 字符串替换

Spark - 寻找重叠值或寻找共同朋友的变体