热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

排班问题——NurseRosteringProblem(NRP)实战

文章目录背景问题调研工具查找——找巨人的肩膀ActionCodeexampleor-tools的基本用法参数调整画甘特图总结背景上次周末碰到女朋友在排班表,花了快一


文章目录

  • 背景
    • 问题调研
    • 工具查找——找巨人的肩膀
  • Action
    • Code example
      • or-tools的基本用法
      • 参数调整
    • 画甘特图
  • 总结


背景

上次周末碰到女朋友在排班表,花了快一个小时,就想着看用代码帮她节省点时间。


问题调研

上网查了一下,看了几篇论文了解了背景。


http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.1030.5363&rep=rep1&type=pdf
https://arxiv.org/pdf/1804.05002.pdf


排班之类的问题都统称为 Nurse Rostering Problem(NRP)问题,就复杂性而言,这是一个NP-hard问题


P问题是在多项式时间内可以被解决的问题,而NP问题是在多项式时间内可以被验证其正确性的问题


多项式时间指的是什么?我理解就是平时计算的算法复杂度

O(1) – constant-time
O(log_2(n)) – logarithmic-time
O(n) – linear-time
O(n^2) – quadratic-time
O(n^k) – polynomial-time
O(k^n) – exponential-time
O(n!) – factorial-time

关于NP问题的描述可以看看我找到的这篇文章


提及复杂性理论,是因为更精准地分析问题后,才能找到合适的工具。

这类问题我们就会想到用计算机科学里面的方法去处理,例如machine learning。


工具查找——找巨人的肩膀

机器学习的技术栈,我学过的就是Python中的scikit-learn、TensorFlow和Keras上去做选择

据我了解,这是一个研究了多年的课题了。既然这样,应该是会有现成训练好的模型去做这类事情。

最后的最后,我就找到了Google开发的OR-Tools(Official Site)。它已经有训练过的模型去处理这类排班问题了。


Action


Code example

官方例子Source Code

主要就是对源码做调整了,以满足现实需求。


or-tools的基本用法

摘抄的伪代码,用于了解工作方式

// 选择和定义一个model,然后设置所需的前置数据和限制条件
model = cp_model.CpModel()
model.NewBoolVar(...)
model.AddExactlyOne(...)
model.Add(...)// 选择和定义一个solver,然后针对model进行optimizate
solver = cp_model.CpSolver()
status = solver.Solve(model, solution_printer)// 创建一个矩阵进行拟合
work = {}
for e in range(num_employees):for s in range(num_shifts):for d in range(num_days):work[e, s, d] = model.NewBoolVar('work%i_%i_%i' % (e, s, d))
// 下面是打印结果的算法
if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:print()header = ' 'for w in range(num_weeks):header += 'M T W T F S S 'print(header)for e in range(num_employees):schedule = ''for d in range(num_days):for s in range(num_shifts):if solver.BooleanValue(work[e, s, d]): // NOTE: 获取优化算法出来的结果schedule += shifts[s] + ' 'print('worker %i: %s' % (e, schedule))print()

参数调整

例子源码中参数注释都很清楚地标明了用法了,适当对现实问题的需要作出调整就行了。下面伪代码只是针对参数作了一些说明。

num_employees = 4 // 员工数,改为目标的员工数
num_weeks = 5 // 工作天数,目标是排一个月的班表,改为5
shifts = ['O', 'M', 'A', 'N'] // 班期,和目标一样# Fixed assignment: (employee, shift, day).
# 固定前两天的,排班前得动态调整下
fixed_assignments = [(0, 0, 0),(1, 1, 0),(2, 2, 0),(3, 3, 0),(0, 0, 1),(1, 1, 1),(2, 2, 1),(3, 3, 1),
]# Request: (employee, shift, day, weight)
# 员工想要固定休息的时间(位置),权重值我理解为代表拟合时可调整的优先级
requests = [# Employee 3 does not want to work on the first Saturday (negative weight for the Off shift).(3, 0, 5, -2),# Employee 2 does not want a night shift on the first Friday (positive weight).(2, 3, 4, 4)
]# Shift constraints on continuous sequence :
# (shift, hard_min, soft_min, min_penalty,
# soft_max, hard_max, max_penalty)
# hard_min: 硬性限制,在周期内最少连续要上的班期天数
# soft_min: 软性限制,在周期内最少连续要上的班期天数
# soft_max和hard_max同上去理解
shift_constraints = [# One or two consecutive days of rest, this is a hard constraint.(0, 1, 1, 0, 2, 2, 0),# betweem 2 and 3 consecutive days of night shifts, 1 and 4 are# possible but penalized.(3, 1, 2, 20, 3, 4, 5),
]# Weekly sum constraints on shifts days:
# (shift, hard_min, soft_min, min_penalty,
# soft_max, hard_max, max_penalty)
weekly_sum_constraints = [# 每周最少要休息的天数(0, 1, 2, 7, 2, 3, 4),
]# Penalized transitions:
# (previous_shift, next_shift, penalty (0 means forbidden))
penalized_transitions = [# 尽量避免午班换晚班(2, 3, 4),# 晚班不能接着早班(3, 1, 0),
]# daily demands for work shifts (morning, afternon, night) for each day
# of the week starting on Monday.
# 每个班期最少要有多少人,我这里的现实问题是有一个人就行了
weekly_cover_demands = [(1, 1, 1), # Monday(1, 1, 1), # Tuesday(1, 1, 1), # Wednesday(1, 1, 1), # Thursday(1, 1, 1), # Friday(1, 1, 1), # Saturday(1, 1, 1), # Sunday
]# Penalty for exceeding the cover constraint per shift type.
excess_cover_penalties = (2, 2, 5)num_days = num_weeks * 7
num_shifts = len(shifts)

画甘特图

技术栈中,选择了matplot去画甘特图,也是现学现卖。

# Generate plot image. [reference: https://www.geeksforgeeks.org/python-basic-gantt-chart-using-matplotlib]shifts_colors = ['white', 'tab:green', 'tab:orange', 'tab:blue']if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:offset = 2# Declaring a figure "gnt"fig, gnt = plt.subplots()# Setting Y-axis limitsgnt.set_ylim(0, num_employees * 15)# Setting X-axis limitsgnt.set_xlim(1, num_days + offset)# Setting labels for x-axis and y-axisgnt.set_xlabel('Dates')gnt.set_ylabel('Employees')# Setting ticks on y-axis# gnt.set_yticks([15, 25, 35, 45])gnt.set_xticks(list(range(1, num_days + offset, 1)))gnt.set_yticks(list(range(15, (num_employees + 1) * 10 + 5, 10)))# Labelling tickes of y-axisgnt.set_yticklabels(employees_names)# Setting graph attributegnt.grid(True)for e in range(num_employees):for d in range(num_days):for s in range(num_shifts):if solver.BooleanValue(work[e, s, d]):gnt.broken_barh([(d+1, d+2)], (10*(e+1), 9),facecolors=(shifts_colors[s]))plt.savefig("gantt1.png")

结果:
在这里插入图片描述


总结


  • 业界很强大
  • 充分理解问题才能便于找到工具
  • 幸运的是这个topic已经有研究,通过已经有研究过的课题,熟悉工具和解决方法的思路,才能更好地去解决其他问题

实战代码:https://github.com/pascallin/NRP-solver


推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了多因子选股模型在实际中的构建步骤,包括风险源分析、因子筛选和体系构建,并进行了模拟实证回测。在风险源分析中,从宏观、行业、公司和特殊因素四个角度分析了影响资产价格的因素。具体包括宏观经济运行和宏经济政策对证券市场的影响,以及行业类型、行业生命周期和行业政策对股票价格的影响。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
author-avatar
1397527971_3148ce
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有