基于Cplex的人员排班问题建模求解(JavaAPI)
使用Java调用Cplex实现了阿里mindopt求解器的案例(https://opt.aliyun.com/platform/case)人员排班问题。
这里写目录标题
- 人员排班问题
- 问题描述
- 数学建模
- 编程求解(Cplex+JavaAPI)
- 求解结果
人员排班问题
随着现在产业的发展,7*24小时服务的需要,人员排班的问题,逐渐成为了企业管理中的重要环节。人员排班在许多行业都具有广泛的应用价值,主要包括以下几个方面:
- 制造业:生产车间的人员分配、班次安排和轮班计划等,需要根据产线的工作要求和员工的技能特点进行合理的排班。
- 医疗行业:医院、诊所等机构需要对医生、护士等员工进行排班。
- 餐饮业:餐厅、咖啡馆等服务场所需要根据客流高峰期和低谷期合理安排员工的工作时间。
- 零售业:商场、超市等零售场所需要根据营业时间、客流量和节假日等因素进行人员排班。
- 旅游业:景区、酒店等旅游设施需要根据旅游旺季、淡季和客流量变化对员工进行排班。
- 客服中心:呼叫中心、在线客服等服务机构需要根据客户咨询需求进行员工排班。
总之,人员排班在各行各业都具有重要的实际应用价值,可以帮助企业和机构提高管理效率、降低成本,同时提升员工的工作满意度和整体效能。总之,人员排班在各行各业都具有重要的实际应用价值,可以帮助企业和机构提高管理效率、降低成本,同时提升员工的工作满意度和整体效能。
运筹学中的数学规划方法是计算人员排班问题的一个好方案。人员排班问题在建模时需要考虑多种约束条件,比如:
- 用工需求约束:根据各岗位的工作任务和生产要求,保证每个岗位在每个时间段内有足够的员工进行工作。
- 员工能力约束:不同岗位可能需要不同的技能和经验,需要确保安排到相应岗位的员工具备相关的能力和资质。
- 工作时间约束:员工的工作时间需要遵守相关法律法规,比如每天工作时间上限、休息时间要求等。此外,还需要考虑员工的工作时间偏好,如部分员工可能只能接受特定时间段的工作安排。
- 连续工作天数约束:为保证员工的工作质量和身体健康,通常要求连续工作天数不超过一定限制。以及员工在一定时间周期内有休假要求,需要确保他们的休假安排得到满足。
- 公平性约束:为保障员工的权益,要求在满足以上约束的前提下,尽量平衡各员工的工作时间和任务分配,避免出现工作负担不均衡的情况。
- 员工偏好:如每个员工有自己更喜欢的上班的时间、岗位、或者协作同事配合等。
我们需要考虑企业内各岗位的需求、员工的工作能力以及工作时间的限制等因素。此外,还需关注企业成本与员工满意度的权衡,以确保在合理控制成本的前提下,最大程度地提高员工的工作满意度。属于一个约束复杂,且多目标的问题。在用数学规划方法进行排班时,建议做一些业务逻辑简化问题,否则容易出现问题太大或者不可解的情况。
下面我们将通过一个简单的例子,讲解如何使用数学规划的方法来做人员排班。
问题描述
个公司有客服岗工作需要安排,不同时间段有不同的用户需求。该公司安排员工上班的班次有三种:早班8-16点、晚班16-24点和夜班0-8点。一周员工最多安排5天上班,最少休息2天。需要保障值班员工能满足需求,且要保障员工休息时间,如前一天安排晚班后,第二天不能安排早班。
请问怎么安排总上班的班次最少,此时的班表是什么样的?
数学建模
编程求解(Cplex+JavaAPI)
复制代码不能直接运行,需要在IDEA pom.xml中导入阿帕奇读取csv文件的依赖,并且需要导入cplex.jar。
数据可在文章开头阿里mindopt案例地址中获取。
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.7</version></dependency>
package main.java;import ilog.concert.*;
import ilog.cplex.IloCplex;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;import java.util.logging.Logger;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.IntStream;public class EmpSchedulingProblem {public int n_employees;public int n_days;public int n_shifts;int[] days;int[] shifts;int[] employees;int[][] demandOfEmployees;public static Logger logger = Logger.getLogger("myLogger");/*** @param day 某天* @param shift 某个班次* @return 某天某班次需求的人数*/public int getDemandOfEmployees(int day, int shift) {return demandOfEmployees[day][shift];}public EmpSchedulingProblem() throws IOException {demandOfEmployees = this.readFile();employees = IntStream.range(0, n_employees).toArray();days = IntStream.range(0, n_days).toArray();shifts = IntStream.range(0, n_shifts).toArray();}public int[][] readFile() throws IOException {this.n_shifts = 0;try (Reader reader = Files.newBufferedReader(Paths.get("src/main/java/mindoptdemo/班次.csv"))) {Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(reader);records.iterator().next(); // 跳过第一行for (CSVRecord record : records) {String shift = (record.get(0)); // 星期1到星期7,索引为0,故-1n_shifts += 1;}} catch (IOException e) {logger.warning(e.getMessage());}// 调度周期:7天,3班倒this.n_days = (int) Files.lines(Paths.get(new File("src/main/java/mindoptdemo/需求人数.csv").getPath())).count() - 1;int[][] day_shift_empNum = new int[n_days][n_shifts];// commons-csv读取csv文件,需要导入依赖try (Reader reader = Files.newBufferedReader(Paths.get("src/main/java/mindoptdemo/需求人数.csv"))) {Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(reader);records.iterator().next(); // 跳过第一行for (CSVRecord record : records) {int day = Integer.parseInt(record.get(0)) - 1; // 星期1到星期7,索引为0,故-1int morningShiftEmpNum = Integer.parseInt(record.get(1)); // 早班需要员工的数量int middleShiftEmpNum = Integer.parseInt(record.get(2)); // 中班需要员工的数量int nightShiftEmpNum = Integer.parseInt(record.get(3)); // 晚班需要员工的数量//保存至二维数组,某天某班次需要的员工数量day_shift_empNum[day][0] = morningShiftEmpNum;day_shift_empNum[day][1] = middleShiftEmpNum;day_shift_empNum[day][2] = nightShiftEmpNum;this.n_employees += morningShiftEmpNum + middleShiftEmpNum + nightShiftEmpNum;}this.n_employees = (int) Math.ceil((double) (this.n_employees) / 5) + 1;
// System.out.println("预估排班人数:" + n_employees);logger.info("预估排班人数:" + n_employees);} catch (IOException e) {logger.info(e.getMessage());}System.out.println(Arrays.deepToString(day_shift_empNum));return day_shift_empNum;}public void cplexSolve() {try {// 声明cplex优化模型IloCplex model = new IloCplex();// 声明决策变量,x_ijk表示员工i在第j天上班次kIloIntVar[][][] x = new IloIntVar[n_employees][n_days][n_shifts];for (int i = 0; i < n_employees; i++) {for (int j = 0; j < n_days; j++) {for (int k = 0; k < n_shifts; k++) {// boolVar()声明x_ijk为0-1变量x[i][j][k] = model.boolVar();}}}// 约束:每天各个班次在岗的人数符合需求for (int d = 0; d < days.length; d++) {for (int s = 0; s < shifts.length; s++) {IloLinearIntExpr expr = model.linearIntExpr();for (int e = 0; e < n_employees; e++) {// addTerm()表示 1*x_edsexpr.addTerm(1, x[e][d][s]);}model.addGe(expr, this.getDemandOfEmployees(d, s));}}// 约束:每人每天最多只有一个班次for (int n : employees) {for (int d : days) {IloLinearIntExpr expr = model.linearIntExpr();for (int s : shifts) {expr.addTerm(1, x[n][d][s]);}model.addLe(expr, 1);}}// 约束:前一天是晚班的,第二天不能是早班for (int e : employees) {for (int d : days) {IloLinearIntExpr expr = model.linearIntExpr();// 0 早班// 1 中班// 2 晚班// 当天上晚班的员工,第二天不能上早班expr.addTerm(1, x[e][d][2]);if (d == 6) {expr.addTerm(1, x[e][0][0]);} else {expr.addTerm(1, x[e][d + 1][0]);}model.addLe(expr, 1);}}// 约束:一周工作工作时间不能超过5天for (int e = 0; e < n_employees; e++) {IloLinearIntExpr expr = model.linearIntExpr();for (int d = 0; d < days.length; d++) {for (int s = 0; s < shifts.length; s++) {expr.addTerm(1, x[e][d][s]);}}model.addLe(expr, 5);}// 目标:雇佣的员工最少,即有排班的班次总数最少IloLinearIntExpr expr = model.linearIntExpr();for (int e : employees) {for (int d : days) {for (int s : shifts) {expr.addTerm(1, x[e][d][s]);}}}model.addMinimize(expr);// 打印求解结果if (model.solve()) {System.out.println("num of employees: " + n_employees);System.out.println("solution status: " + model.getStatus());System.out.println("solution value: " + model.getObjValue());System.out.printf("%-8s", " ");for (int d = 0; d < n_days; d++) {System.out.printf("\t%d", d + 1);}System.out.println();for (int e : employees) {System.out.printf("employee%d\t", e + 1);int shiftCount = 0;for (int d : days) {int shift = 0;for (int s : shifts) {if (((int) model.getValue(x[e][d][s])) != 0) {shift = s + 1;shiftCount += 1;}}System.out.printf("%d\t", shift);}System.out.printf("员工%d这周上%d个班次", e + 1, shiftCount);System.out.println();}}model.end();} catch (IloException e) {logger.warning(e.getMessage());}}public static void main(String[] args) {try {EmpSchedulingProblem esp = new EmpSchedulingProblem();esp.cplexSolve();} catch (IOException e) {throw new RuntimeException(e);}}
}
求解结果
每个员工在那一天上第几个班,如图所示,如员工1-周1-不上班,员工2-周2-夜班;0不上班、1早班、2晚班、3夜班。
相关文章:

基于Cplex的人员排班问题建模求解(JavaAPI)
使用Java调用Cplex实现了阿里mindopt求解器的案例(https://opt.aliyun.com/platform/case)人员排班问题。 这里写目录标题 人员排班问题问题描述数学建模编程求解(CplexJavaAPI)求解结果 人员排班问题 随着现在产业的发展&#…...
理解Go中的数据类型
引言 数据类型指定了编写程序时特定变量存储的值的类型。数据类型还决定了可以对数据执行哪些操作。 在本文中,我们将介绍Go的重要数据类型。这不是对数据类型的详尽研究,但将帮助您熟悉Go中可用的选项。理解一些基本的数据类型能让你写出更清晰、性能…...

【人工智能导论】线性回归模型
一、线性回归模型概述 线性回归是利用函数对一个或多个自变量和因变量之间关系进行建模的一种回归分析。简单来说,就是试图找到自变量与因变量之间的关系。 二、线性回归案例:房价预测 1、案例分析 问题:现在要预测140平方的房屋的价格&…...

十大常见排序算法详解(附Java代码实现和代码解析)
文章目录 十大排序算法⛅前言🌱1、排序概述🌴2、排序的实现🌵2.1 插入排序🐳2.1.1 直接插入排序算法介绍算法实现 🐳2.1.2 希尔排序算法介绍算法实现 🌵2.2 选择排序🐳2.2.1 选择排序算法介绍算…...

在Ubuntu上通过Portainer部署微服务项目
这篇文章主要记录自己在ubuntu上部署自己的微服务应用的过程,文章中使用了docker、docker-compose和portainer,在部署过程中遇到了不少问题,因为博主也是初学docker-compose,通过这次部署实战确实有所收获,在这篇文章一…...

软件测试基础学习
注意: 各位同学们,今年本人求职目前遇到的情况大体是这样了,开发太卷,学历高的话优势非常的大,公司会根据实际情况考虑是否值得培养(哪怕技术差一点);学历稍微低一些但是技术熟练的…...
移动手机截图,读取图片尺寸
这个代码的设计初衷是为了解决图片处理过程中的一些痛点。想象一下,我们都曾遇到过这样的情况:相机拍摄出来的照片、网络下载的图片,尺寸五花八门,大小不一。而我们又渴望将它们整理成一套拥有统一尺寸的图片,让它们更…...
服务器应用程序不可用的原因是什么引起的
服务器应用程序不可用的原因是什么引起的 服务器应用程序不可用的原因是什么引起的?其实服务器应用程序不可用可能是由多种原因引起的。主要包括软件故障、网络问题、硬件故障、安全问题、配置错误、容量不足、数据库问题等,具体详细服务器应用程序不可用的原因如下…...
使用SPY++查看窗口信息去排查客户端UI软件问题
目录 1、使用SPY++查看窗口的信息 2、使用SPY++查看某些软件UI窗口用什么UI组件实现的...

Flink CDC MySQL同步MySQL错误记录
1、启动 Flink SQL [appuserwhtpjfscpt01 flink-1.17.1]$ bin/sql-client.sh2、新建源表 问题1:Encountered “(” 处理方法:去掉int(11),改为int Flink SQL> CREATE TABLE t_user ( > uid int(11) NOT NULL AUTO_INCREMENT COMME…...

深入了解 Linux 中的 AWK 命令:文本处理的瑞士军刀
简介 在Linux和Unix操作系统中,文本处理是一个常见的任务。AWK命令是一个强大的文本处理工具,专门进行文本截取和分析,它允许你在文本文件中查找、过滤、处理和格式化数据。本文将深入介绍Linux中的AWK命令,让你了解其基本用法和…...
【RuoYi项目分析】网关的AuthFilter完成“认证”,注意是认证而不是权限
文章目录 1. 功能介绍2. AuthFilter的配置3. AuthFilter实现分析4. 资料参考 过滤器的功能是检验经过网关的每一个请求,检查 token 中的信息是否有效。 注意是“认证检查”,而不是“权限” 1. 功能介绍 1、在用户完成登录后,程序会把用户相关…...
excel将文件夹下面的表格文件指定名称的sheet批量导出到指定文件中,并按照文件名保存在新文件的不同sheet中
excel将文件夹下面的表格文件指定名称的sheet批量导出到指定文件中,并按照文件名保存在新文件的不同sheet中 import pandas as pd import ositems os.listdir("./") sheetname"" for item in items:if item.__contains__(xls):dfpd.read_exc…...

IIS管理器无法打开。启动后,在任务栏中有,但是窗口不见了
找到IIS管理器启动程序的所在位置 并在cmd命令行中调用 inetmgr.exe /reset 进行重启 先查看IIS管理器属性,找到其位置 管理员模式打开cmd命令行,并切换到上面的文件夹下运行Inetmgr.exe /reset 运行完成后可以重新看到IIS窗口 原因:由于某…...

使用VBA实现快速模糊查询数据
实例需求:基础数据保存在Database工作表中,如下图所示。 基础数据有37个字段,上图仅展示部分字段内容,下图中黄色字段为需要提取的数据字段。 在Search工作表B1单元格输入查询关键字Title和Genre字段中搜索关键字,包…...
spring boot flowable多人前加签
1、前加签插件 package com.xxx.flowable.cmd;import com.xxx.auth.security.user.SecurityUser; import com.xxx.commons.ApplicationContextHolder; import com.google.common.collect.Lists; import org.apache.commons.collections.CollectionUtils; import org.apache.co…...

结构体运算符重载
1.降序 struct Point{int x, y;//重载比较符bool operator < (const Point &a) const{return x > a.x;//当前元素大时,是降序} };2.升序 struct Point{int x, y;//重载比较符 // bool operator < (const Point &a) const{ // return x…...
幽默直观的文档作者注释
注释是程序中非常重要的一部分,在不同的编程语言中,注释的风格和语言描述会有所不同。以下是一些常用的注释风格和语言描述: 直观注释:这种注释使用简洁、明了的语言,帮助读者快速地理解代码。例如,代码中…...

前端开发网站推荐
每个人都会遇见那么一个人,永远无法忘却,也永远不能拥有。 以下是一些可以用来查找和比较前端框架的推荐网站: JavaScript框架比较: 这些网站提供了对不同JavaScript框架和库的详细比较和评估。 JavaScripting: 提供了大量的JavaS…...
【C语言】通讯录管理系统(保姆级教程+内含源码)
C系列文章目录 目录 C系列文章目录 前言 一,模块化编程 二,系统框架构建 1.成员信息的创建 2.菜单实现 3.系统功能声明 三、系统功能实现 1.初始化通讯录 2.增加联系人 3.显示所有联系人 4.根据姓名查找位置 5.删除指定联系人 6.查找指定联…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...