功能开发背景
港口货轮需要进行集装箱的装卸任务:
船上的每一个集装箱,可以用三个维度的坐标来唯一定位:(bay, column, layer),这三个维度结合其他一些固有信息,构成了一个箱子的字段属性,存储在箱子数据表中。
现在需要以bay为单位,对bay内所有column、layer下的箱子,按照特有作业优先级进行作业顺序的编号(优先级从高到低:40尺-单钩箱子>20尺-双钩箱子>20尺-单钩箱子)。
即某个bay下,所有column最上层的箱子,统计他们的优先级,取其优先级最高的特定优先级进行作业,等到所有column最高层的箱子没有该优先级的箱子,按照低一级的优先级进行作业。如此循环往复三个优先级,直到所有的箱子都被编上作业顺序。
实现思路
大意如此,细节上的约束限制条件,不展开描述
- 获取船舶所有的bay信息
- 针对每一个bay信息,通过Django ORM查询数据库并返回满足条件的箱子集合
- 获取这些箱子集合包含的column、layer,并存储在列表中
- 两层循环遍历每一个箱子(按照层数和列数:从上到下(层),从左到右(列)遍历):
不用双层遍历的算法难度太大,构思不出来
for lay in layers:
for col in columns:
pass
- 在 优先级i 遍历的过程中,如果某列的箱子优先级不是i,则标记该列,在i优先级的后续遍历过程中不再考虑该列的箱子。
代码实现细节带来的速度差异
箱子数量计数
# 老版
for lay in layers:
for col in lay_columns[lay]:
boxes_selected = temp_queryset.filter(box_column=col, box_layer=lay)
# ========================================
len_boxes_selected = boxes_selected.count()
# ========================================
if len_boxes_selected == 2:
if boxes_selected[0].is_work_together == 1:
self.boxes_number_1(boxes_selected)
else:
self.boxes_number_2(boxes_selected)
elif len_boxes_selected == 1:
self.boxes_number_1(boxes_selected)
else:
pass
# 新版
for lay in layers:
for col in lay_columns[lay]:
boxes_selected = temp_queryset.filter(box_column=col, box_layer=lay)
# ========================================
len_boxes_selected = len(boxes_selected)
# ========================================
if len_boxes_selected == 2:
if boxes_selected[0].is_work_together == 1:
self.boxes_number_1(boxes_selected)
else:
self.boxes_number_2(boxes_selected)
elif len_boxes_selected == 1:
self.boxes_number_1(boxes_selected)
else:
pass
对计数后的查询集进行修改操作,用len()函数要比count()函数,效率高一点
参考文章:https://blog.csdn.net/qq_39187019/article/details/108170726
箱子列和层数的获取方式
# 老版
layer = [86, 84, 82, 80, 78, 76]
column = [16, 14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11, 13, 15]
# 新版
layer = [86, 84, 82, 80, 78, 76]
column = {
86:[16, 14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11, 13, 15],
84:[14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11],
82:[16, 14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11, 13, 15],
80:[10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11, 13, 15],
78:[16, 14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7, 9, 11, 13, 15],
76:[16, 14, 12, 10, 8, 6, 4, 2, 0, 1, 3, 5, 7]
}
老版:获取这个bay下所有箱子里面,涉及到的列和层
- 该方法十分方便的获取列和层
- 但是存在冗余,特定列,特定层下是没有箱子的,比如:(84, 16)
新版:获取这个bay下所有箱子里面,涉及到的层,再获取特定层下面涉及到的列信息
- 该方法相较于老版,在获取列信息上会更加耗时
- 但是不存在冗余,只要是遍历到的层、列,就一定能保证有箱子
整体计算下来,新版更加节省时间
结果存储方式的不同
# 老版本
self.numbered_boxes |= changed_queryset
# 新版本
for box in changed_queryset:
self.numbered_boxes.append(box)
老版:对已经编号的箱子集合进行并集操作
- 最终的存储结果:<class ‘django.db.models.query.QuerySet’>
新版:在对箱子编号的时候,对箱子进行append操作
- 最终的存储结果:[<class ‘icwp_basic_data.models.Ship_Box_Data’>, <class ‘icwp_basic_data.models.Ship_Box_Data’>, …]
运算结果对比
上述改动,都是逐一改变的,每次改变都会有时间上的节省。
下边仅展示最终结果
数据量:10307条数据
运算时间
版本 | 运算时长(s) |
---|---|
老版 | 316 |
新版 | 143 |
老版
新版