基于技能公平分配任务给工人的算法

Posted

技术标签:

【中文标题】基于技能公平分配任务给工人的算法【英文标题】:Algorithm for fairly assigning tasks to workers based on skills 【发布时间】:2011-06-13 11:53:04 【问题描述】:

(在任何人问之前,这不是家庭作业。)

我有一组有兴趣的工人,即:

鲍勃:Java、XML、Ruby

苏珊:Java、html、Python

弗雷德:Python、Ruby

山姆:Java、Ruby

(实际上每个工人有 10-25 个“兴趣”,我有大约 40-50 个工人)

同时,我有大量任务需要分配给工作人员。每个任务必须分配给至少 3 名工人,并且工人必须至少符合一项任务的兴趣:

任务 1:Ruby、XML 任务 2:XHTML、Python

等等。所以 Bob、Fred 或 Sam 可以获得任务 1; Susan 或 Fred 可以获得任务 2。

因此所有这些都存储在数据库中:

Task
    id integer primary key
    name varchar

TaskInterests
    task_id integer
    interest_id integer

Workers
    id integer primary key
    name varchar
    max_assignments integer

WorkerInterests
    worker_id
    interest_id

Assignments
    task_id
    worker_id
    date_assigned

每个员工最多可以完成 10 个任务。有些兴趣比其他兴趣更罕见(即只有 1 或 2 名员工将其列为兴趣),有些兴趣更常见(即一半工人列出他们)。

算法必须

将每个任务分配给 3 名工人(它是 假设至少有 3 个 工人对其中一项感兴趣 任务的兴趣)。 为每位员工分配一项或多项任务

理想情况下,算法将:

为每个工作人员分配与其最大分配和任务总数成比例的任务。例如,如果苏珊说她会做 20 个任务,而大多数人只会做 10 个任务,而有 50 个工人和 300 个任务,那么她应该被分配 12 个任务 (20/10*(300/50))。 为每位员工分配各种任务,因此如果 Susan 列出 4 个兴趣,她将获得包含 4 个兴趣的任务(而不是获得 10 个具有相同兴趣的任务)

到目前为止,最困难的方面是处理这些问题:

与少数相应工作人员感兴趣的任务 兴趣不大的工人,尤其是 兴趣不大,任务相对较少的工人

【问题讨论】:

这是一个很好的问题,但我很好奇您是否可以更具体地了解您要优化的内容。您是否想要最大化或最小化某些特定值?如果是这样,你能告诉我们它是什么吗?现在这是一个有趣的问题,但我认为它有点不明确。 我们的目标确实是更公平地分配任务。目前还没有正式的算法,更多的是蛮力“遍历任务,首先按匹配工人最少的任务排序,然后分配给工人,按他们已经分配的数量排序”这最终得到了一些工人的任务太多或太少。 【参考方案1】:

对于难以找到直接解决方案的问题,使用近似算法、评估函数和改进解决方案的方法可能是一个好主意。方法有很多种,比如genetic algorithms和simulated annealing。

基本思想是使用某种简单的算法(例如贪心算法)来获得一些模糊可用的东西并进行随机修改,保留那些提高评估分数的修改并丢弃那些使评估分数变得更糟的修改。

使用遗传算法生成一组(例如 100 个)随机解决方案并进行评分,并保留并“培育”最好的解决方案,以产生具有与前几代相似的特征但具有一些随机突变的新一代解决方案。

对于模拟退火,最初接受稍差的解决方案的概率很高,但随着时间的推移会降低。这降低了早期陷入局部最优的风险。

【讨论】:

【参考方案2】:

所以我对这个问题进行了一些思考,我认为您可以通过将其减少为 min-cost max-flow 的实例来获得一个好的解决方案(对于“好”的一些定义)(例如,参见 this )。思路如下。假设你有一组工作 J 作为输入,每个工作都有一组必要的技能,还有一组工人 W,每个人都有一组才能。您还为每个工人提供了一个常数 k_i,表示您希望他们做多少工作,以及一个常数 m_i,表示您可以分配给他们的最大工作数量。您的目标是以这样一种方式将工作分配给工人,即每项工作都由具有技能的工人完成,没有工人做的工作超过 m_i 个工作,并且工人完成的“多余”工作的数量被最小化.例如,如果有 5 名工人,每人想做 4 项任务,并且负载均衡,使得 2 名工人做 4 份工作,1 人做 3 份工作,1 人做 5 份工作,则总超额为 1,因为 1 名工人多做 1 份。工作超出预期。

减少如下。现在,我们将忽略平衡要求,只看汤姆如何将其减少到最大流量;我们将在最后添加负载平衡。用指定的起始节点 s 和汇节点 t 构造一个图 G。将每个作业 j 和每个工人 w 的节点添加到该图中。从 s 到这 j 个成本为零且容量为 1 的每个节点都有一条边。从每个 w 节点到 t 也会有一条边,成本为零,容量为 m_i。最后,对于每个工作 j 和工人 w,如果工人 w 具有完成工作 j 所需的才能,则从 j 到 w 存在成本为零且能力为 1 的边。

我们的想法是,我们希望通过 j 和 w 节点将流从 s 推送到 t,这样通过某个 j 节点到 w 节点的每条流路径都意味着应该将作业 j 分配给工人 w。从 s 到 j 节点的边的容量限制确保最多有一个单元流进入 j 节点,因此作业最多只分配一次。从 w 个节点到节点 t 的边上的容量限制可以防止每个 worker 被分配太多次。由于所有容量都是积分的,因此从 s 到 t 存在一个积分最大流,因此该图中的最大流对应于向工人分配的工作是合法的,并且不超过任何工人的最大负载。您可以通过查看图表中的总流量来检查是否所有作业都已分配;如果它等于作业的数量,则它们都已被分配。

然而,上述结构对平衡工作负载没有任何作用。为了解决这个问题,我们将稍微修改一下构造。不是从每个 w 节点到 t 都有一条边,而是,对于每个 w 节点,将两个节点添加到图中,c 和 e,并将它们连接如下。从 w_i 到 c_i 有一条边,容量为 k_i,成本为零,还有一条从 c_i 到 t 的边相同。还有一条从 w_i 到 e_i 的边,成本为 1,容量为 m_i - k_i。从 e_i 到 t 也有一条容量相等且成本为零的边。

直观地说,我们没有改变离开任何 w 节点的流量,但我们改变了流量的成本。通过 c 节点分流到 t 的流是免费的,因此 worker 可以承担 k_i 个工作而不会产生成本。之后的任何作业都必须通过 e 进行路由,每个流过它的单位都需要花费一个。在这个新图中找到一个最大流仍然可以确定一个分配,但是在图中找到最小成本最大流会找到最小化分配给工人的多余工作的分配。

可以使用一些众所周知的算法在多项式时间内求解最小成本最大流,因此希望这是一个有用的答案!

【讨论】:

非常好的解决方案(它可能应该是公认的答案,尽管我看不到它如何处理工人所需的“各种任务”,但这是一个相对不重要的点)。另外,我认为为了满足 OP 的要求,即每个工作必须由 3 名工人承担,从 s 到 j 的边应该有容量 3(而不是 1)。【参考方案3】:

这个问题可以建模为 Maximum Flow Problem.

在最大流问题中,您有一个有向图,其中包含两个特殊节点,即源和汇。图中的边具有容量,您的目标是在不超过任何边容量的情况下分配从源到汇通过图形的流。

通过(非常)精心制作的图表,我们可以从最大流量中找到满足您要求的作业。

让我给要求编号。

必填:

1. Workers are assigned no more than their maximum assignments.
2. Tasks can only be assigned to workers that match one of the task's interests.
3. Every task must be assigned to 3 workers.
4. Every worker must be assigned to at least 1 task.

可选:

5. Each worker should be assigned a number of tasks proportional to that worker's maximum assignments
6. Each worker should be assigned a variety of tasks.

我假设最大流量是使用 Edmonds-Karp Algorithm.

我们先找一张满足要求1-3的图。

将图形描绘为 4 列节点,其中边仅从列中的节点到右侧相邻列中的节点。

在第一列中,我们有源节点。在下一列中,我们将为每个工作人员提供节点。从源头来看,每个工人都有一条边,其容量等于该工人的最大分配。这将强制执行要求 1。

在第三列中,每个任务都有一个节点。从第二列中的每个工作人员来看,该工作人员感兴趣的每个任务都有一条边,容量为 1(如果工作人员兴趣的交集不为空,则该工作人员对某项任务感兴趣)。这将强制执行要求 2。容量 1 将确保每个工作人员只占用每个任务 3 个插槽中的 1 个。

在第四列中,我们有水槽。从每个任务到容量为 3 的接收器都有一条边。这将强制执行要求 3。

现在,我们使用 Edmonds-Karp 算法在该图中找到最大流量。如果此最大流量小于3 * (# of tasks),则不存在满足要求 1-3 的分配。如果没有,则存在这样的分配,我们可以通过检查最终的增强图来找到它。在扩充图中,如果从任务到容量为 1 的工作人员之间存在一条边,则将该工作人员分配给该任务。


现在,我们将修改我们的图形和算法以满足其余要求。

首先,让我们满足要求 4。这将需要对算法进行一些小的更改。最初,将源到工作人员的所有容量设置为 1。在此图中找到最大流量。如果流量不等于工人的数量,则不存在满足要求 4 的分配。现在,在您的最终残差图中,对于每个工人,从源到该工人的边的容量为 0,反向边的容量为 1 . 将它们分别更改为that worker's maximum assignments - 10。现在像以前一样继续 Edmonds-Karp 算法。基本上我们所做的是首先找到一个任务,以便每个工人都被分配到一个任务。然后从该任务中删除反向边缘,以便始终将工作人员分配给至少一个任务(尽管它可能不是在第一遍中分配给的任务)。


现在让我们满足要求5。严格来说,这个要求只是意味着我们将每个工人的最大分配除以sum of all worker's maximum assignments / number of tasks。这很可能不会有令人满意的任务。但没关系。用这些新的最大分配初始化我们的图。运行 Edmonds-Karp。如果它找到一个使从任务到接收器的边缘饱和的流,我们就完成了。否则,我们可以在残差图中增加从 sink 到 worker 的容量并继续运行 Edmonds-Karp。重复直到我们使边缘饱和到水槽中。不要过多地增加容量,以免为工人分配太多任务。此外,从技术上讲,每个工人的增量应该与该工人的最大任务成正比。这些都很容易做到。


最后让我们满足要求 6。这个有点棘手。首先,在工作人员和任务之间添加一列,并删除工作人员到任务的所有边。在这个新列中,为每个工人添加一个节点,用于每个工人的兴趣。从这些新节点中的每一个,为每个任务添加一条具有匹配兴趣且容量为 1 的边。将每个工作人员的一条边添加到其容量为 1 的每个兴趣节点。现在,此图中的一个流将强制执行,如果一个工作人员分配给 n 个任务,则这些任务的利益与该工人利益的联合的交集大小至少为 n。同样,可能有一个令人满意的任务没有这个任务,但没有一个有它。我们可以像需求 5 一样处理这个问题:运行 Edmonds-Karp 直到完成,如果没有令人满意的分配,增加从工人到他们的兴趣节点的容量并重复。

请注意,在这个修改后的图表中,我们不再满足要求 3,因为如果他们的兴趣交集的大小大于 1,则单个工作人员可能会被分配到任务的多个/所有槽。我们可以解决这个问题。在兴趣节点和任务节点之间添加新的节点列,并删除这些节点之间的边。对于每个员工,在新列中为每个任务插入一个节点(因此每个员工对于每个任务都有自己的节点)。从这些新节点到其右侧对应的任务,添加一条容量为 1 的边。从每个工作人员的兴趣节点到该工作人员的任务节点,从每个兴趣到每个匹配的任务添加一条容量为 1 的边。

-

编辑:让我试着澄清一下。设-(n)-> 是一条容量为 n 的边。

以前我们为每个具有匹配兴趣的工作任务对设置了worker-(1)->task。现在我们有worker-(k)->local interest-(1)->local task-(1)->global task。现在,您可以考虑将任务与工人兴趣对相匹配。第一条边说,对于一个工人来说,它的每一个兴趣都可以与k 任务相匹配。第二条优势是每个工人的兴趣只能与每个工作匹配一次。第三条边表示每个任务只能分配给每个工人一次。请注意,您可以将多个流程从工作人员推送到本地任务(等于他们的兴趣交叉点的大小),但由于第三条边,只有 1 个流程从工作人员到全局任务节点。

-

还请注意,我们不能真正将这种递增与要求 5 的递增正确混合。但是,对于工人->兴趣边的每个容量 1,2,...,r,我们可以运行一次整个算法。然后,我们需要一种对作业进行排名的方法。也就是说,当我们放宽要求 5 时,我们可以更好地满足要求 6,反之亦然。不过,我更喜欢另一种方法来放松这些限制。


更好的需求放松方法 (inspired-by/taken-from templatetypedef)

当我们希望能够放宽多个要求(例如 5 和 6)时,我们可以将其建模为最小成本最大流量问题。这可能比我上面描述的增量搜索更简单。

例如,对于需求 5,将所有边的成本设置为 0。我们有从源到工人的初始边,其容量等于 worker's maximum assignments / (sum of all worker's maximum assignments / number of tasks),成本为 0。然后您可以添加另一条边该工作人员的剩余容量和成本 1. 另一种可能性是使用某种累进成本,例如当您向工作人员添加任务时,向该用户添加另一个任务的成本会上升。例如。您可以改为将工作人员的剩余容量分成单独的边,成本为1,2,3,4,...

对于需求 6,可以在工作节点和本地兴趣节点之间执行类似的操作。需要平衡权重以反映不同需求的相对重要性。

这种方法也足以强制执行要求 4。此外,要求 5 的成本可能应该与工人的最大任务成正比。然后,将 1 个额外任务分配给最大 100 的工人不会像分配额外任务给最大 2 的工人那样多。


复杂性分析

n = # of employeesm = # of tasksk = max interests for a single task/workerl = # of interestsj = maximum of maximum assignments

要求 3 意味着 n = O(m)。我们还假设l = O(m)j = O(m)

在较小的图中(在更改 req. 6 之前),该图有n + m + 2 = O(m) 顶点和最多n + m + k*min(n, m) = O(km) 边。

更改后它有2 + n + n * l + n * m + m = O(nm) 顶点和n + k * n + k * m * n + n * m + m = O(kmn) 边(从技术上讲,我们可能需要j * n + j * l 更多节点和边,以便从一个节点到另一个节点没有多个边,但这不会改变渐近边界)。另请注意,没有边缘需要容量 > j。

使用最小成本最大流量公式,我们可以在O(VEBlogV) 中找到解决方案,其中V = # verticesE = # edgesB = max capacity on a single edge。在我们的例子中,这给出了O(kjn^2m^2log(nm))

【讨论】:

有一些标准算法可以解决最大流问题,在某些边缘的流上有下界;这会简化分析和描述吗?另外,你能澄清一下第六步是如何工作的吗?看起来这可能会使一些存在合法分配的问题不再有解决方案。 另外,是否有理由选择 Edmonds-Karp 而不是快速推送重新标记变体? 我认为 Edmonds-Karp 是因为我很了解它。我不知道我用于满足要求 4 的方法是否适用于其他算法。 @templatetypedef 我为第六步添加了一点说明。如果不清楚,我可以解释更多。 哇,通读这个答案需要一些时间!这就是 CS 学位真正有帮助的地方。 :)【参考方案4】:

尝试将您的任务映射到stable marriage problem。任务成为准妻子`,你的员工成为追求者。

您可能想要添加一些额外的算法来将每个任务的偏好分配给员工,反之亦然 - 您可以为每个任务的组件分配一些理想的熟练度,然后让您的员工对每个任务进行排名。您可以为每个员工拥有的每个组件分配一个熟练程度,并使用它来获取员工的每个任务偏好。

一旦你有了偏好,然后运行算法,发布结果,然后允许人们成对向你申请以交换任务 - 毕竟这是一个人的问题,当人们有一定程度的控制。

【讨论】:

其实这是一个“这些是你的任务,你会喜欢它们”的问题——这不是单个“工人”可以挑选的场景,“工人”的数量是巨大的,“任务”的数量也是巨大的,因此,事后的改变真的是不可行的。这就是为什么我们在第一次就尽可能正确地做到这一点如此重要。 然后错过工人互动部分。我看过稳定婚姻问题的求解算法是用来映射实习生到美国医院的,所以有这个容量。 进来最终将一个标记为答案。请注意,这并不是因为其他两个也不是很好的答案,只是这个可能是最容易尝试的。

以上是关于基于技能公平分配任务给工人的算法的主要内容,如果未能解决你的问题,请参考以下文章

公平分布式资源分配共识推荐算法

[转] max-min fairness 最大最小公平算法

将相同的队列项分配给多个工作人员

max-min fairness 最大最小公平算法

众包算法

调度算法