使用其他约束计算分配

时间:2013-07-05 12:30:42

标签: algorithm mathematical-optimization

假设我有一群学生和几位教授。每个学生都需要和其中一位教授一起参加口试。为此,每个学生都提供一份(订购的)名单,其中包括三位他更愿意参加考试的教授。当然,每位教授只能提供有限数量的考试。 我可以使用Kuhn-Munkres算法计算一个任务,尽可能多的学生被分配给他们的第一个愿望教授。

现在假设每个学生都必须参加两个考试,并据此提供两个愿望清单。我想再次为尽可能多的学生分配他们的第一个愿望教授,但要受到约束,学生不得同时考试同一个教授

是否有一些算法可以有效地计算最佳分配?也许是Kuhn-Munkres算法的推广?或者这个新问题已经NP难了?人们可以尝试减少这个难题吗?

我应该强调,我正在寻找一个确切的解决方案。很容易想到一些启发式方法,比如考虑一种类型的考试。

2 个答案:

答案 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门考试。

如果学生偏好他们希望与教授一起参加考试,则需要在决策变量中添加下标eX_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之间边缘的容量将限制每位教授的最大工作量。

字典工作负载存储每位教授允许的最大测试数。