这是一个复杂的问题,但我怀疑我可以应用一些原则来简化它 – 我只是不知道它是什么.
我需要在本学期的课堂上安排演讲班.有多个可能的日期和多种演示文稿类型.我进行了一项调查,学生可以对不同的主题进行排名.我想做的是为学生提供最好的(或至少是一个好的)演示文稿插槽.
那么,我有什么:
> 12个日期列表
> 18名学生名单
> CSV文件,其中每个学生(行)的每个日期的评分为1-5
我想得到什么:
>每个学生应该有一个演示类型A(介绍),一个演示类型B(数字)和3个演示类型C(目标)
>每个日期应至少有一种类型的演示文稿
>每个日期不应超过A类型或B类型
>尽量给予学生高度评价的演讲(4或5)
我应该注意到,我意识到这看起来像是一个家庭作业问题,但这是现实生活:-).我想我可以为每个学生制作一个学生班,其中包含每种演示类型的日期,但我不确定填写它的最佳方式是什么.实际上,我甚至不确定从哪里开始.
最佳答案 TL; DR:我认为你给了你的学生太多的选择:D
但无论如何我对这个问题有所了解.实际上很有趣的练习,虽然有些约束有点模糊.最重要的是,我不得不猜测实际学生的偏好分布是什么样的.我使用均匀分布的自变量,尽管这可能不太现实.我仍然认为它应该在实际数据上和在我随机生成的数据上一样有效.
我认为粗暴强迫它,但粗略的分析给了我估计超过10 ^ 65可能的配置.那有点儿了.由于我们没有万亿年的时间来考虑所有这些因素,我们需要一种启发式的方法.
由于问题的严重性,我试图避免做任何回溯.但这意味着你可能会陷入困境;可能没有一个解决方案,每个人只得到他们给4和5的日期.
我最终实现了一个类似于Iterative Deepening的双刃搜索,其中最好的情况是我们仍然抱有希望(即,指派学生给出他们给出5的日期)以及我们愿意接受的最坏情况(一些学生可能不得不忍受3)逐渐降低,直到找到解决方案.如果我们陷入困境,重置,降低期望,并再试一次.首先分配任务A和B,并且仅在A和B完成后才完成C,因为对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%