这是一个复杂的问题,但我怀疑我可以采用一些原则来简化它 - 我只是不知道它是什么。
我需要在本学期的学生班上安排演讲位置。有多个可能的日期和多种演示文稿类型。我进行了一项调查,学生可以对不同的主题进行排名。我想做的是为学生提供最好的(或至少是好的)演讲插槽。
所以,我有:
我想得到什么:
intro
),一个演示文稿类型B(figures
)和3个演示文稿类型C(aims
)我应该注意到,我意识到这看起来像是一个家庭作业问题,但它的现实生活:-)。我想我可以为每个学生创建一个Student
课程,其中包含每种演示文稿类型的日期,但我不确定填写它的最佳方式是什么。实际上,我甚至不知道从哪里开始。
答案 0 :(得分:2)
TL; DR :我认为你给了你的学生太多的选择:D
但无论如何我对这个问题有所了解。实际上很有趣的练习,虽然有些约束有点模糊。最重要的是,我不得不猜测实际学生的偏好分布是什么样的。我使用均匀分布的自变量,尽管这可能不太现实。我认为它在真实数据上的效果与在我随机生成的数据上的效果一样。
我认为粗暴强迫它,但粗略分析给了我估计超过10 ^ 65种可能的配置。那有点儿了。由于我们没有万亿年的时间来考虑所有这些因素,我们需要一种启发式的方法。
由于问题的严重性,我试图避免做任何回溯。但这意味着你可能会陷入困境;可能没有一个解决方案,每个人只能获得他们给出4和5的日期。
我最终实现了一个双刃Iterative Deepening - 类似搜索,其中最佳案例我们仍然抱有希望(即,指派学生到他们给的日期a 5)和我们愿意接受的最差案例场景(一些学生可能不得不忍受3)会逐渐降低,直到找到解决方案。如果我们陷入困境,重置,降低期望,并再试一次。任务A和B首先分配,C只在A和B完成后完成,因为对C的约束要严格得多。
我还使用加权因子来模拟在最大化学生幸福与满足每日演示类型限制之间的权衡。
目前,它似乎找到了几乎所有随机生成的首选项的解决方案。我包括了一个评估指标;所有分配的学生/日期组合的偏好值之和与所有学生理想/前3个偏好值之和之间的比率。例如,如果学生X有两个五,四个,其余三个在他的名单上,并被分配到他的五个和三个三分之一,他得到5 + 3 + 3 = 11但理想情况下可以获得5 + 5 + 4 = 14;他满意度为11/14 = 78.6%。
经过一些测试后,似乎我的实施往往会产生95%左右的平均学生满意度,比我预期的要好很多:)但同样,那就是假数据。真正的偏好可能更集中,更难以满足。
以下是algorihtm的核心。完整的脚本是大约250行,我想这里有点太长了。请查看at Github。
...
# Assign a date for a given task to each student,
# preferring a date that they like and is still free.
def fill(task, lowest_acceptable, spread_weight=0.1, tasks_to_spread="ABC"):
random_order = range(nStudents) # randomize student order, so everyone
random.shuffle(random_order) # has an equal chance to get their first pick
for i in random_order:
student = students[i]
if student.dates[task]: # student is already assigned for this task?
continue
# get available dates ordered by preference and how fully booked they are
preferred = get_favorite_day(student, lowest_acceptable,
spread_weight, tasks_to_spread)
for date_nr in preferred:
date = dates[date_nr]
if date.is_available(task, student.count, lowest_acceptable == 1):
date.set_student(task, student.count)
student.dates[task] = date
break
# attempt to "fill()" the schedule while gradually lowering expectations
start_at = 5
while start_at > 1:
lowest_acceptable = start_at
while lowest_acceptable > 0:
fill("A", lowest_acceptable, spread_weight, "AAB")
fill("B", lowest_acceptable, spread_weight, "ABB")
if lowest_acceptable == 1:
fill("C", lowest_acceptable, spread_weight_C, "C")
lowest_acceptable -= 1
以下是脚本打印的示例结果:
Date
================================================================================
Student | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
================================================================================
1 | | A | B | | C | | | | | | | |
2 | | | | | A | | | | | B | C | |
3 | | | | | B | | | C | | A | | |
4 | | | | A | | C | | | | | | B |
5 | | | C | | | | A | B | | | | |
6 | | C | | | | | | | A | B | | |
7 | | | C | | | | | B | | | | A |
8 | | | A | | C | | B | | | | | |
9 | C | | | | | | | | A | | | B |
10 | A | B | | | | | | | C | | | |
11 | B | | | A | | C | | | | | | |
12 | | | | | | A | C | | | | B | |
13 | A | | | B | | | | | | | | C |
14 | | | | | B | | | | C | | A | |
15 | | | A | C | | B | | | | | | |
16 | | | | | | A | | | | C | B | |
17 | | A | | C | | | B | | | | | |
18 | | | | | | | C | A | B | | | |
================================================================================
Total student satisfaction: 250/261 = 95.00%