From fc996e746c6f565b88f4f8019d20b6d7ef268fed Mon Sep 17 00:00:00 2001 From: happyw1nd Date: Wed, 22 Jan 2025 18:03:10 +0800 Subject: [PATCH] update README.md --- README.md | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b583c8..c190e28 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,93 @@ # EVA 值班排班工具 -说明文档施工中... \ No newline at end of file + +## 环境配置 + +如果你有 `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` 中的相关代码 \ No newline at end of file