示例:对于3个可用作业,10个候选人各自给出2个首选项(第一个比第二个更优先),然后他们的老板必须最佳地分配(并均匀分配)他们均匀根据他们的偏好。显然,不需要的工作需要一些随机抽取。
我如何编写自动计算此最佳分配的算法?
我环顾四周,找到了bipartite graphs,这可能会给我一些线索,但是我无法绕过它!
对于游戏的“运气”方面,我已经实施了一个简单的Fisher Yates Shuffle。
偏好权重: 如果有2个偏好,当分配给工人时,获得第一个选择权重为+2,第二个选择+1,不需要的选择-1(例如)。 “最优性”目标是最大化聚合偏好。
答案 0 :(得分:2)
你的问题非常具有挑战性,但我找到了一个有效的(可能不是最高效的)解决方案。我的示例是用PHP编写的,但您应该能够对其进行调整。我将尝试解释代码背后的“想法”。
注意:好像您之后添加了“10人,3个作业”限制 - 或者我简单地过度了。但是,我的代码应该为您提供一个可能能够适应该约束的示例。我的代码目前假设n
人有n
个工作。 (使其适应10/3标准的最简单方法是将3个工作分成10个相同的工作单元,假设有10个工人!)
首先,让我们创建一些基本的架构。我们需要person
,job
,显然是一个矩阵,代表一个人对工作的满意度。以下剪辑就是这样做的:
<?php
class Person{
var $name;
var $prim;
var $sec;
function __construct($name, $prim, $sec){
$this->name = $name;
$this->prim = $prim;
$this->sec = $sec;
}
function likes($job){
if ($job->type == $this->prim) return 2;
if ($job->type == $this->sec) return 1;
else return -1;
}
}
class Job{
var $name;
var $type;
function __construct($name, $type){
$this->name = $name;
$this->type = $type;
}
}
$persons = array(
"Max" => new Person("Max", "programing", "testing"),
"Peter" => new Person("Peter", "testing", "docu"),
"Sam" => new Person("Sam", "designing", "testing")
);
$jobs = array(
"New Classes" => new Job("New Classes", "programing"),
"Theme change" => new Job("Theme change", "designing"),
"Test Controller" => new Job("Test Controller", "testing")
);
// debug: draw it:
echo "<h2>Happines with Jobs</h2> ";
echo "<table border=1>";
$p=0;
echo "<tr>";
foreach ($jobs AS $job){
$j=0;
foreach ($persons as $person){
if ($p++==0){
echo "<tr><td></td>";
foreach ($persons as $per) {
echo "<td>".$per->name."</td>";
}
echo "</tr>";
}
if ($j++==0){
echo "<td>".$job->name."</td>";
}
echo "<td>".$person->likes($job)."</td>";
}
echo "</tr>";
}
echo "</table>";
这会给你一个这样的表:
第二,我们需要创建所有工作和人员的排列。 (实际上我们不需要,但这样做会告诉你原因,为什么我们不需要!)
要创建所有排列,我们只使用某个人或作业的名称。 (我们可以稍后将名称解析回实际对象)
//build up all permutations
$personNames = array();
foreach ($persons AS $person){
$personNames[] = $person->name;
}
$jobNames = array();
foreach ($jobs AS $job){
$jobNames[] = $job->name;
}
$personsPerms = array();
pc_permute($personNames,$personsPerms);
$jobsPerms = array();
pc_permute($jobNames,$jobsPerms);
function pc_permute($items, &$result, $perms = array( )) {
if (empty($items)) {
$result[] = join('/', $perms);
} else {
for ($i = count($items) - 1; $i >= 0; --$i) {
$newitems = $items;
$newperms = $perms;
list($foo) = array_splice($newitems, $i, 1);
array_unshift($newperms, $foo);
pc_permute($newitems,$result, $newperms);
}
}
}
现在,我们有2个阵列:所有工作排列和所有人的排列。 对于上面给出的示例,数组将如下所示(每个3个元素,使每个数组3 * 2 * 1 = 6个排列):
Array
(
[0] => Max/Peter/Sam
[1] => Peter/Max/Sam
[2] => Max/Sam/Peter
[3] => Sam/Max/Peter
[4] => Peter/Sam/Max
[5] => Sam/Peter/Max
)
Array
(
[0] => New Classes/Theme change/Test Controller
[1] => Theme change/New Classes/Test Controller
[2] => New Classes/Test Controller/Theme change
[3] => Test Controller/New Classes/Theme change
[4] => Theme change/Test Controller/New Classes
[5] => Test Controller/Theme change/New Classes
)
现在,我们可以创建一个nXn
表,其中包含所有可能的工作分配的总体满意度的所有值:
// debug: draw it:
echo "<h2>Total Happines of Combination (full join)</h2> ";
echo "<table border=1>";
$p=0;
echo "<tr>";
$row = 0;
$calculated = array();
foreach ($jobsPerms AS $jobComb){
$j=0;
$jobs_t = explode("/", $jobComb);
foreach ($personsPerms as $personComb){
if ($p++==0){
echo "<tr><td></td>";
foreach ($personsPerms as $n) {
echo "<td>".$n."</td>";
}
echo "</tr>";
}
if ($j++==0){
echo "<td>".$jobComb."</td>";
}
$persons_t = explode("/", $personComb);
$h = 0;
echo "<td>";
for ($i=0; $i< count($persons_t); $i++){
$h += $persons[$persons_t[$i]]->likes($jobs[$jobs_t[$i]]);
}
echo $h;
echo "</td>";
}
$col=0;
$row++;
echo "</tr>";
}
echo "</table>";
让我们称这个矩阵为“M”
此矩阵包含“批次”的双重组合:(a / b)TO(1/2)等于(b / a)到(2/1)等...
毕竟:我们简单可以忽略:
忽略所有列&gt; 1:
echo "<h2>Total Happines of Combination (ignoring columns)</h2> ";
echo "<table border=1>";
$p=0;
echo "<tr>";
$row = 0;
$calculated = array();
foreach ($jobsPerms AS $jobComb){
$j=0;
$jobs_t = explode("/", $jobComb);
$col = 0;
$personComb = $personsPerms[0];
if ($p++==0){
echo "<tr><td></td>";
echo "<td>".$personsPerms[0]."</td>";
echo "</tr>";
}
if ($j++==0){
echo "<td>".$jobComb."</td>";
}
$persons_t = explode("/", $personComb);
$h = 0;
echo "<td>";
for ($i=0; $i< count($persons_t); $i++){
$h += $persons[$persons_t[$i]]->likes($jobs[$jobs_t[$i]]);
}
echo $h;
echo "</td>";
$col=0;
$row++;
echo "</tr>";
}
echo "</table>";
输出:
你去吧!在这个例子(其中一个)中,最令人满意的解决方案是:
- &GT;幸福:6。
还有其他相同的分布。
示例:6人/ 6个工作:
$persons = array(
"Max" => new Person("Max", "programing", "testing"),
"Peter" => new Person("Peter", "testing", "docu"),
"Sam" => new Person("Sam", "designing", "testing"),
"Jeff" => new Person("Jeff", "docu", "programing"),
"Fred" => new Person("Fred", "programing", "designing"),
"Daniel" => new Person("Daniel", "designing", "docu")
);
$jobs = array(
"New Classes" => new Job("New Classes", "programing"),
"Theme change" => new Job("Theme change", "designing"),
"Test Controller" => new Job("Test Controller", "testing"),
"Create Manual" => new Job("Create Manual", "docu"),
"Program more!" => new Job("Program more!", "programing"),
"Style the frontend" => new Job("Style the frontend", "designing")
);
结果(人物:Max / Peter / Sam / Jeff / Fred / Daniel)
答案 1 :(得分:1)
假设通过“均匀分配”表示您知道必须为每个项目分配多少人,这是weighted matching problem (又名“最大基数二分匹配”)。只需将每个打开的位置(而不是每个作业)视为一个节点 - 因此,具有3个位置的作业将具有三个节点。
维基百科文章提供了几种解决方案。
答案 2 :(得分:-1)
伪代码
for(n to number of jobs left) { job n = a random candidate if(random candidate first preference == job n) remove random candidate from list and remove job from list } if(jobs left) { for(n to number of jobs left) for(i to number of candidates) if(candidate first preference == job n) { job n = candidate i remove candidate i from list and remove job n from list } else if(candidate second preference == job n) { job n = candidate i } }