EVA_duty_arrange_tool/README.md

93 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# EVA 值班排班工具
## 环境配置
如果你有 `conda`,推荐新建一个虚拟环境:
```bash
conda create -n EvaDutyArrangeTool python=3.8
conda activate EvaDutyArrangeTool
```
如果不新建虚拟环境也可以,本项目对 `python` 的版本不是很敏感。
安装项目中所需要的所有包的最新版本。其中 `pyside6` 是一个前端库,`pyinstaller` 用于打包项目,`ortools` 是一个高效的组合优化求解器,`pandas` 用于处理表格数据:
```bash
pip install pyside6 pyinstaller ortools pandas
```
如果发现以上包的最新版本与代码不兼容,可以尝试使用我开发时使用的版本:
```bash
pip install pyside6==6.6.3.1 pyinstaller==6.11.1 ortools==9.11.4210 pandas==2.0.3
```
## 项目结构
```
EVA_duty_arrange_tool/
├─ main.py // 主函数,定义了前端界面和组件的回调函数
├─ solve.py // 定义了值班排班问题的求解函数
├─ utils.py // 定义了读取、写入 excel 的函数
├─ pics // 储存了说明文档中用到的图片
│ ├─ *.jpg/*.png
├─ *.md // 说明文档
├─ *.xlsx // 测试用例
```
## 项目运行&打包
项目运行方式:
```bash
python main.py
```
本项目使用 `pyinstaller` 工具进行打包。如果要打包,请确保能够正常运行项目。打包命令如下:
```bash
pyinstaller --onefile --windowed --name=EVA_duty_arrange_tool main.py
```
## 数学原理
本项目将值班排班问题建模为了一个[组合优化](https://zh.wikipedia.org/wiki/%E7%BB%84%E5%90%88%E4%BC%98%E5%8C%96)问题。
在本问题中,优化目标是:
- 让每一班的同学数量尽可能平均
在本问题中,约束是:
1. 让每位同学每周的班次数符合意愿
2. 让每位同学在自己想要的时间段值班
3. 每班次至少(多)包含若干位技术部老人
4. 每班次至少(多)包含若干位人资部小朋友
5. 等等...
下面我将用数学语言建模以上定义的优化目标和约束。设一共有 $n$ 位同学,$m$ 个值班的班次,定义以下符号:
- 令 $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$ 班
- 令 $tech_{i} \in \{0,1\}, \quad i=1,2\dots n$ 表示第 $i$ 位同学是否是技术部成员
- 令 $old_{i} \in \{0,1\}, \quad i=1,2\dots n$ 表示第 $i$ 位同学是否是老人
- 令 $hr_{i} \in \{0,1\}, \quad i=1,2\dots n$ 表示第 $i$ 位同学是否是人资部成员
以上符号中,只有 $x_{ij}$ 是待求解的变量,其余均为已知量。
则优化目标为:
$$ \min_{x_{ij}}\left | M_j - \frac{\sum_{i=1}^{n}Ni }{m} \right | $$
也即,最小化每一班的值班人数与平均每一班值班人数之差的绝对值。
> Tips: 在组合优化问题的定义中,只能定义线性的式子,是不允许出现“绝对值”运算的。所以需要引入辅助变量 $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$。此时优化目标变为:$\min_{x_{ij},a_j}\sum_{j=1}^{m}a_j$。
接下来定义约束:
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}tech_{i}old_{i} \le t_{max}, \quad j=1,2\dots m$$
4. 每班次至少包含 $h_{min}$ 位人资部小朋友,至多包含 $h_{max}$ 位人资部小朋友
$$h_{min} \le \sum_{i=1}^{n}x_{ij}hr_{i}(1-old_{i}) \le h_{max}, \quad j=1,2\dots m$$
5. 其他更多的限制也是类似的,这里就略过了。
以上完成了整个排班问题的数据建模。建模完成后,用任何组合优化求解器都能都求解问题。在本项目中,我使用了 `ortools` 这个谷歌开发的组合优化求解器。`ortools` 支持 `c++``python``c#``java` 等多种语言,也有跨平台支持,个人感觉比较好用。
## 维护指南
- 如果你想更改 excel 的读取、写入相关的功能,应该修改 `utils.py` 中的相关函数。
- 如果你想更改软件的前端界面,应该修改 `main.py``MyWidget` 这个类相关的代码
- 如果你想更换排班问题的建模方式、更换求解器、增减限制条件,应该修改 `solve.py` 中的相关代码