浙江大学学生E志者协会值班排班工具
Go to file
Dawn1Ocean 58361ac6f1
refactor; algorithm
2025-10-15 23:11:25 +08:00
pics v1.1.0 2025-01-17 01:40:38 +08:00
.gitignore update gitignore 2025-01-22 12:52:29 +08:00
.python-version uv support; refactor of utils.py; fix: want_num 2025-10-13 23:11:25 +08:00
README.md refactor; algorithm 2025-10-15 23:11:25 +08:00
main.py refactor; algorithm 2025-10-15 23:11:25 +08:00
pyproject.toml refactor; algorithm 2025-10-15 23:11:25 +08:00
solve.py refactor; algorithm 2025-10-15 23:11:25 +08:00
utils.py refactor; algorithm 2025-10-15 23:11:25 +08:00
uv.lock refactor; algorithm 2025-10-15 23:11:25 +08:00
使用手册.md refactor; algorithm 2025-10-15 23:11:25 +08:00
问卷星结果_样例输入.xlsx first commit 2025-01-12 18:24:17 +08:00
问卷星结果_较极端样例.xlsx uv support; refactor of utils.py; fix: want_num 2025-10-13 23:11:25 +08:00

README.md

EVA 值班排班工具

环境配置

本项目使用 uv 进行包管理。

安装项目中所需要的所有包的最新版本。其中 pyside6 是一个前端库,pyinstaller 用于打包项目,ortools 是一个高效的组合优化求解器,pandas 用于处理表格数据:

uv sync

项目结构

EVA_duty_arrange_tool/
    ├─ main.py                // 主函数,定义了前端界面和组件的回调函数
    ├─ solve.py               // 定义了值班排班问题的求解函数
    ├─ utils.py               // 定义了读取、写入 excel 的函数
    ├─ pics                   // 储存了说明文档中用到的图片
    │  ├─ *.jpg/*.png
    ├─ *.md                   // 说明文档
    ├─ *.xlsx                 // 测试用例

项目运行 & 打包

项目运行方式:

uv run main.py

本项目使用 pyinstaller 工具进行打包。如果要打包,请确保能够正常运行项目。打包命令如下:

uvx pyinstaller --onefile --windowed --name=EVA_duty_arrange_tool main.py

数学原理

本项目将值班排班问题建模为了一个组合优化问题。

在本问题中,优化目标是:

  • 一个涉及多个方面的量化指标
    • 每一班同学数量的平均程度
    • 每一班包含技术部老人的个数
    • 每一班包含技术部小朋友的个数
    • 每一班包含人资部小朋友的个数

在本问题中,约束是:

  1. 让每位同学每周的班次数符合意愿
  2. 让每位同学在自己想要的时间段值班
  3. 每班次至少(多)包含若干位技术部成员
  4. 等等...

下面我们将用数学语言建模以上定义的优化目标和约束。设一共有 n 位同学,m 个值班的班次,协会共有 t 个部门,定义以下符号:

  • x_{ij} \in \{0,1\}, \quad i=1,2\dots n,\quad j=1,2\dots m 表示最终第 i 位同学是否值第 j
  • M_j = \sum_{i=1}^{n} x_{ij}, \quad j=1,2,\dots m 表示第 j 班次实际安排的值班人数。
  • N_i, \quad i=1,2,\dots n 表示第 i 个同学每周愿意值班次数
  • v_{ij} \in \{0,1\}, \quad i=1,2\dots n,\quad j=1,2\dots m 表示第 i 位同学是否有空值第 j
  • \text{old}_{i} \in \{0,1\}, \quad i=1,2\dots n 表示第 i 位同学是否是老人
  • d_{ik}\in\{0,1\} \quad i=1,2\dots n,\quad k=1,2\dots t 表示第 i 位同学是否属于第 k 个部门,目前顺序为电脑部、电器部、人资部、财外部、文宣部

以上符号中,只有 x_{ij} 是待求解的变量,其余均为已知量。

则优化目标为:

  1. 每一班的值班人数与平均每一班值班人数之差的绝对值尽可能小

     X_1=\sum\left | M_j - \frac{\sum_{i=1}^{n}Ni }{m}  \right | 

    在组合优化问题的定义中,只能定义线性的式子,是不允许出现“绝对值”运算的。所以需要引入辅助变量 a_j \in [0,+\infty ),并额外引入两组约束:a_j \ge M_j-\frac{\sum_{i=1}^{n}Ni }{m}a_j \ge \frac{\sum_{i=1}^{n}Ni }{m} - M_j。那么在优化的过程中,a_j 就会逐渐趋向 X_1

  2. 每一班技术部老人数量要达到一定值

     X_2=\sum x_{ij}\cdot(d_{i1}+d_{i2})\cdot\text{old}_i  
  3. 每一班技术部小朋友数量要达到一定值

     X_3=\sum x_{ij}\cdot(d_{i1}+d_{i2})\cdot(1-\text{old}_i)  
  4. 每一班人资部小朋友数量要达到一定值

     X_4=\sum x_{ij}\cdot d_{i3}\cdot(1-\text{old}_i)  
  5. 每一班各个部门的人数尽可能平均,这里使用每一班中人数最多部门与人数最少部门的差

    引入辅助变量 C_{jk}=\sum_{i=1}^{n}x_{ij}\cdot d_{ik}

     X_5=\sum  

接下来定义约束:

  1. 让每位同学每周的班次数符合意愿
    \sum_{j=1}^{m}x_{ij}=N_i, \quad i=1,2\dots n
  2. 让每位同学在自己想要的时间段值班
    x_{ij} \le v_{ij}, \quad i=1,2\dots n,\quad j=1,2\dots m
  3. 每班次至少包含 t_{min} 位技术部成员,至多包含 t_{max} 位技术部成员
    t_{min} \le \sum_{i=1}^{n}x_{ij}\cdot tech_{i} \le t_{max}, \quad j=1,2\dots m
  4. 其他更多的限制也是类似的,这里就略过了。

以上完成了整个排班问题的建模。建模完成后,用任何组合优化求解器都能都求解问题。在本项目中,我们使用了 ortools 这个谷歌开发的组合优化求解器。ortools 支持 C++PythonC#Java 等多种语言,也有跨平台支持。

维护指南

  • 如果你想更改 Excel 的读取、写入相关的功能,应该修改 utils.py 中的相关函数。
  • 如果你想更改软件的前端界面,应该修改 main.pyMyWidget 这个类相关的代码。
  • 如果你想更换排班问题的建模方式、更换求解器、增减限制条件,应该修改 solve.py 中的相关代码。