假设我有一群学生和几位教授。每个学生都需要和其中一位教授一起参加口试。为此,每个学生都提供一份(订购的)名单,其中包括三位他更愿意参加考试的教授。当然,每位教授只能提供有限数量的考试。 我可以使用Kuhn-Munkres算法计算一个任务,尽可能多的学生被分配给他们的第一个愿望教授。
现在假设每个学生都必须参加两个考试,并据此提供两个愿望清单。我想再次为尽可能多的学生分配他们的第一个愿望教授,但要受到约束,学生不得同时考试同一个教授。
是否有一些算法可以有效地计算最佳分配?也许是Kuhn-Munkres算法的推广?或者这个新问题已经NP难了?人们可以尝试减少这个难题吗?
我应该强调,我正在寻找一个确切的解决方案。很容易想到一些启发式方法,比如考虑一种类型的考试。
答案 0 :(得分:2)
您可以使用整数编程将此完全建模为Assignment Problem.
<强>变量强>
让 s 学生和 p 教授。
决策变量
X_sp is 1 if student s takes an exam with professor p
0 otherwise
Let Limit_p be the number of exams that professor p can handle.
处理学生偏好
我们可以通过费用(目标)来处理每个学生的偏好
Let C_sp be the 'cost' of assigning student s to take an exam with professor p.
随着偏好的降低,我们会不断提高成本。
C_sp = 1 if p is the *first* preference of s
C_sp = 2 if p is the *second* preference of s
C_sp = 3 if p is the *third* preference of s
...
C_sp = n if p is the *n th* preference of s
<强>配方强>
案例1:一次考试
Min (sum over s)(sum over p) C_sp X_sp
subject to:
(sum over p) X_sp = 1 for each student s # Every student must take an exam
(sum over s) X_sp <= Limit_p for each professor p # Every prof can only handle so many exams
X_sp = {0,1} # binary
案例2:学生参加两门考试
Min (sum over s)(sum over p) C_sp X_sp
subject to:
(sum over p) X_sp = 2 for each student s # Every student must take an exam
(sum over s) X_sp <= Limit_p for each professor p # Every prof can only handle so many exams
X_sp = {0,1} # binary
照顾没有学生可以同时参加同一位教授的考试
通常情况下,我们必须引入指标变量 Y_sp
来表示学生是否参加过p教授的考试。但是,在这种情况下,我们甚至不必这样做。 X_sp
是二进制的这一事实将确保没有学生最终与同一位教授一起参加2门考试。
如果学生偏好他们希望与教授一起参加考试,则需要在决策变量中添加下标e
。 X_spe
您可以通过匈牙利方法解决这个问题,或者更容易解决这个问题,任何可用的LP / IP解算器实现。
Hungarian algorithm's复杂度为O(n ^ 3),并且由于我们没有引入任何边约束来破坏完整性属性,线性解决方案将始终是整数。< / p>
更新(一点理论)
为什么解决方案是完整的?为了理解为什么解决方案保证是完整的,需要一点理论。
通常,当面对IP时,首先想到的是尝试解决线性松弛(忘记积分值要求并尝试。)这将给出最小化问题的下限。通常有一些所谓的复杂约束使整数解决方案很难找到。一种解决方法是通过将它们抛到目标函数来解决这些问题,并解决更简单的问题(拉格朗日松弛问题。)
现在,如果拉格朗日的解决方案不会改变你是否具有完整性(就像分配问题一样)那么你总是得到整数解。
对于那些真正有兴趣学习Integrality和Lagrangian对偶的人,this reference is quite readable.第3节中的例子专门介绍了作业问题。
免费的MILP解决方案: This SO question is quite exhaustive for that.
希望有所帮助。
答案 1 :(得分:1)
我认为你可以通过解决最小成本流问题来最佳地解决这个问题。
下面的Python代码说明了如何使用networkx库为3名学生完成此操作。
import networkx as nx
G=nx.DiGraph()
multiple_prefs=[{'Tom':['Prof1','Prof2','Prof3'],
'Dick':['Prof2','Prof1','Prof3'],
'Harry':['Prof1','Prof3','Prof1']},
{'Tom':['Prof2','Prof1','Prof3'],
'Dick':['Prof2','Prof1','Prof3'],
'Harry':['Prof1','Prof3','Prof1']}
]
workload={'Prof1':2,'Prof2':10,'Prof3':4}
num_tests=len(multiple_prefs)
num_students=len(multiple_prefs[0])
G.add_node('dest',demand=num_tests*num_students)
A=[]
for i,prefs in enumerate(multiple_prefs):
testname='_test%d'%i
for student,proflist in prefs.items():
node=student+testname
A.append(node)
G.add_node(node,demand=-1)
for i,prof in enumerate(proflist):
if i==0:
cost=0 # happy to assign first choices
elif i==1:
cost=1 # Slightly unhappy to assign second choices
else:
cost=4 # Very unhappy to assign third choices
n2=prof+'_with_'+student
n3=prof
G.add_edge(node,n2,capacity=1,weight=cost) # Edge taken if student takes this test with the professor
G.add_edge(n2,n3,capacity=1,weight=0) # Capacity stops student taking both tests with the same professor
G.add_edge(n3,'dest',capacity=workload[prof],weight=0) # Capacity stops professor exceeding his workload
flowdict = nx.min_cost_flow(G)
for student in A:
for prof,flow in flowdict[student].items():
if flow:
print student,'with',prof
关键是我们为学生和教授的每个组合都有一个单独的节点(在代码中称为n2)。通过向目的地提供n2的容量1,我们确保每个学生只能对该教授进行一次测试。
字典multiple_prefs存储两个字典。第一个字典包含第一个测试的每个学生的首选项列表。第二个是第二次测试的偏好。
修改
现在代码还包括每个教授的节点n3。这些节点和dest之间边缘的容量将限制每位教授的最大工作量。
字典工作负载存储每位教授允许的最大测试数。