当前位置: 首页 > news >正文

Johnson算法实战:如何用Python优化流水线作业调度(附完整代码)

Johnson算法实战:用Python优化流水线作业调度

在制造业和计算机科学领域,流水线作业调度是一个经典问题。想象一下,当多个产品需要经过两台机器按相同顺序加工时,如何安排加工顺序才能让所有产品最快完成?这正是Johnson算法要解决的核心问题。

1. 理解流水线调度问题

流水线调度问题可以描述为:有n个作业需要在两台机器M1和M2上加工,每个作业必须先经过M1加工,再经过M2加工。已知每个作业在M1和M2上的加工时间分别为a_i和b_i,如何安排作业顺序使得总加工时间最短?

这个问题的复杂性在于:

  • 机器依赖:M2必须等待M1完成当前作业才能开始加工
  • 顺序影响:不同的作业顺序会导致不同的总完工时间
  • 资源竞争:当M1加工下一个作业时,M2可能仍在加工前一个作业

关键指标是最小化makespan(最后一个作业在M2上完成的时间)。通过分析,我们发现最优调度应该:

  • 尽可能让M1连续工作(无空闲)
  • 最小化M2的空闲时间

2. Johnson算法原理剖析

Johnson算法通过巧妙分类和排序,能在O(nlogn)时间内找到最优调度方案。其核心思想是将作业分为两类,并分别按不同规则排序:

2.1 作业分类规则

  1. N1类作业:a_i < b_i(在M1上加工时间较短的作业)
  2. N2类作业:a_i ≥ b_i(在M2上加工时间较短或相等的作业)

2.2 排序策略

  • N1类作业:按a_i非递减排序(加工时间短的先做)
  • N2类作业:按b_i非递增排序(加工时间长的先做)
def classify_jobs(a, b): N1 = [] # a_i < b_i N2 = [] # a_i >= b_i for i in range(len(a)): if a[i] < b[i]: N1.append((a[i], b[i], i)) else: N2.append((a[i], b[i], i)) return N1, N2

2.3 Johnson不等式

算法有效性基于Johnson不等式:对于最优调度中的任意相邻作业i和j,必须满足:

min(b_i, a_j) ≥ min(b_j, a_i)

这保证了交换相邻作业不会得到更优解。

3. Python实现Johnson算法

下面我们实现完整的Johnson算法,包含作业分类、排序和调度时间计算。

3.1 算法实现

def johnson_schedule(a, b): n = len(a) # 步骤1:分类作业 N1 = [] N2 = [] for i in range(n): if a[i] < b[i]: N1.append((a[i], b[i], i)) else: N2.append((a[i], b[i], i)) # 步骤2:排序 N1_sorted = sorted(N1, key=lambda x: x[0]) # N1按a_i升序 N2_sorted = sorted(N2, key=lambda x: -x[1]) # N2按b_i降序 # 步骤3:合并序列 schedule = N1_sorted + N2_sorted order = [job[2] for job in schedule] # 计算总时间 time_m1 = 0 time_m2 = 0 for job in schedule: time_m1 += job[0] time_m2 = max(time_m1, time_m2) + job[1] return order, time_m2

3.2 示例测试

考虑4个作业的加工时间:

  • 作业0: (3, 6)
  • 作业1: (4, 2)
  • 作业2: (8, 9)
  • 作业3: (10, 15)
a = [3, 4, 8, 10] b = [6, 2, 9, 15] order, makespan = johnson_schedule(a, b) print("最优作业顺序:", order) # 输出: [1, 0, 2, 3] print("最短完成时间:", makespan) # 输出: 35

4. 算法性能分析与优化

4.1 时间复杂度

步骤操作时间复杂度
1分类作业O(n)
2排序作业O(nlogn)
3合并序列O(n)
4计算时间O(n)

总时间复杂度为O(nlogn),主要由排序步骤决定。

4.2 空间复杂度

需要额外O(n)空间存储分类后的作业列表,属于原地排序算法。

4.3 优化技巧

  1. 并行计算:对于大规模作业,可以并行处理分类和排序
  2. 提前终止:如果发现所有作业都属于同一类,可以简化排序过程
  3. 内存优化:使用生成器避免存储中间结果
# 优化后的分类函数 def classify_jobs_optimized(a, b): N1 = ((a_i, b_i, i) for i, (a_i, b_i) in enumerate(zip(a, b)) if a_i < b_i) N2 = ((a_i, b_i, i) for i, (a_i, b_i) in enumerate(zip(a, b)) if a_i >= b_i) return N1, N2

5. 实际应用案例

5.1 汽车制造流水线

假设某汽车工厂有焊接(M1)和喷漆(M2)两个工序,5款车型的加工时间(小时)如下:

车型焊接(a_i)喷漆(b_i)
A57
B34
C82
D66
E45

应用Johnson算法:

  1. N1类:B(3,4), E(4,5), A(5,7)
  2. N2类:D(6,6), C(8,2)
  3. 排序后顺序:B(3)→E(4)→A(5)→C(8)→D(6)
  4. 总完工时间计算:
a = [3, 4, 5, 8, 6] b = [4, 5, 7, 2, 6] order, time = johnson_schedule(a, b) print(f"最优生产顺序: {order}, 总时间: {time}小时")

5.2 计算机任务调度

在计算机系统中,两个连续的处理阶段(如CPU计算和磁盘I/O)也可以建模为流水线调度问题。假设有6个任务:

任务计算时间I/O时间
T125
T273
T362
T447
T569
T682

Johnson算法给出的最优顺序可以显著减少总处理时间,提高系统吞吐量。

6. 算法扩展与变种

虽然Johnson算法最初针对两机流水线问题,但其思想可以扩展到更复杂场景:

6.1 三机流水线问题

当作业需要经过M1→M2→M3三台机器时,在满足以下条件之一时仍可使用Johnson算法:

  1. M2的所有作业处理时间相同
  2. M2的作业处理时间是M1和M3处理时间的线性组合

6.2 并行机器情况

对于多台并行M1和多台并行M2的情况,问题变为NP难问题,需要结合启发式算法:

  1. CDS算法:将问题分解为多个两机问题
  2. NEH启发式:基于作业处理时间总和排序

6.3 动态作业到达

当作业不是一次性全部到达时,需要在线调度算法:

  • First Come First Served (FCFS)
  • Shortest Processing Time first (SPT)

提示:在实际应用中,Johnson算法常作为更复杂调度算法的基准或组成部分。

7. 可视化分析与比较

为了直观理解Johnson算法的优势,我们比较随机调度、SPT规则和Johnson算法的效果:

7.1 甘特图比较

import matplotlib.pyplot as plt import numpy as np def plot_gantt(a, b, order, title): n = len(order) m1_times = [a[i] for i in order] m2_starts = [] m2_ends = [] time_m1 = 0 time_m2 = 0 for i in order: time_m1 += a[i] time_m2 = max(time_m1, time_m2) + b[i] m2_starts.append(time_m2 - b[i]) m2_ends.append(time_m2) fig, ax = plt.subplots(figsize=(10, 3)) for i in range(n): ax.broken_barh([(sum(m1_times[:i]), m1_times[i])], (10, 9), facecolors='tab:blue') ax.broken_barh([(m2_starts[i], b[order[i]])], (20, 9), facecolors='tab:orange') ax.set_yticks([15, 25]) ax.set_yticklabels(['M1', 'M2']) ax.set_xlabel('时间') ax.set_title(f'{title} (总时间: {time_m2})') plt.show() # 比较三种调度方式 random_order = np.random.permutation(len(a)) spt_order = sorted(range(len(a)), key=lambda x: a[x]) johnson_order, _ = johnson_schedule(a, b) plot_gantt(a, b, random_order, "随机调度") plot_gantt(a, b, spt_order, "SPT调度") plot_gantt(a, b, johnson_order, "Johnson调度")

7.2 性能对比数据

调度策略平均完成时间最坏情况比率
随机调度O(n²)无保证
SPT规则O(nlogn)2-近似
JohnsonO(nlogn)最优解

8. 常见问题与解决方案

在实际应用中,可能会遇到以下问题:

Q1:当多个作业具有相同的加工时间时,如何保证唯一解?A:可以添加次要排序键,如作业ID,确保排序稳定性。

Q2:如何处理机器故障或作业优先级?A:需要扩展基本算法,可以考虑:

  1. 为作业添加优先级权重
  2. 引入机器可用时间窗口

Q3:Johnson算法能否处理作业有准备时间的情况?A:经典算法不支持,但可以修改为:

def johnson_with_setup(a, b, setup): # 将准备时间合并到加工时间中 a_modified = [a_i + setup_i for a_i, setup_i in zip(a, setup)] return johnson_schedule(a_modified, b)

9. 工程实践建议

  1. 数据预处理:检查输入数据有效性,处理异常值
  2. 算法健壮性:处理边界情况(如空输入、单作业)
  3. 性能监控:对于大规模问题,实现进度指示
  4. 结果验证:添加验证函数检查调度可行性
def validate_schedule(a, b, order): time_m1 = 0 time_m2 = 0 for i in order: time_m1 += a[i] if time_m1 > time_m2: time_m2 = time_m1 + b[i] else: time_m2 += b[i] return time_m2 # 使用示例 a = [3, 4, 8, 10] b = [6, 2, 9, 15] order, _ = johnson_schedule(a, b) assert validate_schedule(a, b, order) == 35

10. 进一步优化方向

对于追求极致性能的场景,可以考虑:

  1. Cython加速:将关键部分用Cython实现
  2. 多线程排序:对于超大规模作业(n>1e6)
  3. GPU加速:使用CUDA实现并行排序
  4. 近似算法:当n极大时,使用抽样方法
# cython_johnson.pyx cimport cython from libc.stdlib cimport qsort @cython.boundscheck(False) @cython.wraparound(False) def cython_johnson(int[:] a, int[:] b): cdef int n = a.shape[0] # 实现C级别的快速分类和排序 # ...

Johnson算法以其简洁性和高效性,在运营管理、生产调度、计算系统等领域有着广泛应用。掌握其原理和实现,能为解决实际调度问题提供有力工具。

http://www.cnnetsun.cn/news/1309955.html

相关文章:

  • RK3576、RK3588、RK3568:三款主流AIoT芯片的精准选型与场景适配指南
  • 泰凌微TLSR825X定时器实战:从硬件配置到软件轮询的完整指南
  • PyTorch进阶(15)-- torch.flatten()方法的深度解析与实战应用
  • GPT-3提示工程实战:从零开始构建高效prompt的5个技巧(附Playground示例)
  • 【VS Code】Windows10下VS Code搭建Java开发环境全攻略
  • 提升效率:用快马AI一键生成模块化计算机组成原理模拟器框架
  • ChatGLM3-6B功能体验:智能缓存技术,刷新页面无需重载模型
  • Phi-3-mini-128k-instruct助力软件测试:自动生成测试用例与缺陷报告
  • Vue3+ElementPlus避坑指南:el-pagination的total必须用Number类型?
  • 利用ABAP BAPI与OLE自动化,构建SE11对象批量生成与模板管理工具
  • PCIe 流量控制机制:信用管理的艺术
  • Realistic Vision V5.1写实模型效果对比:V5.0 vs V5.1在手部结构与发丝表现差异
  • 反射体系实战
  • CLIP-GmP-ViT-L-14算法精讲:深入理解对比学习与图文预训练核心技术
  • 第 178 场双周赛Q1:101014. 找到第一个唯一偶数
  • 基于RA - AF的高斯混合聚类裂纹模式识别:MATLAB实现
  • java8案例对list[过滤、分组,转换,查找等]清洗逻辑
  • Csimplecleaner:C盘维护与空间管理的最佳实践
  • 智能科学与技术毕业设计2026开题指导
  • LeetCode热题100 括号生成
  • 项目实训。
  • 开关磁阻电机SRM12-8技术详解:额定功率达2200w,转速稳定达额定转速3450rpm
  • MATLAB环境下基于随机游走拉普拉斯算子的快速谱聚类方法 算法运行环境为MAYLAB R2018A
  • 神经网络PID控制BP_PID,模糊PID控制等Matlab/SImulink建模仿真
  • 2026-03-16 GitHub 热点项目精选
  • 计算机文件基础:从概念到路径实践
  • 螺杆式空压机工频运行,变频机不能用使用西门子224xp 十昆仑通态触摸屏,程序有注释
  • KEPServerEX 6.6中文版下载|稳定运行|含详细安装与教程
  • 【什么是二叉树?什么是二叉堆?】
  • 冒泡,选择,插入排序再学习