优化算法|遗传算法求解作业车间调度问题(Python)

本文介绍了作业车间调度问题的背景和数学模型,重点阐述了如何使用遗传算法解决此问题,包括初始化种群、计算适应度、选择策略、交叉和变异操作,以及提供了一个Python实现示例。

作者简介:本人擅长运筹优化建模及算法设计,包括各类车辆路径问题、生产车间调度、二三维装箱问题,熟悉CPLEX和gurobi求解器

微信公众号:运筹优化与学习

如有运筹优化相关建模或代码定制需求,可通过微信公众号联系我们

前言

之前的推文都是介绍车辆路径问题以及装箱问题,我们以这篇为引子,开始作业车间调度问题及其求解算法学习之路。本推文将展示遗传算法求解作业车间调度问题的求解思路以及python代码。

作业车间调度问题简介

排产调度(scheduling)是生产制造业和服务业的重要决策对象,旨在目标明确的前提下,合理地将资源分配给任务。作业车间调度问题(Job-Shop Scheduling Problem,简称 JSSP)就是典型的排产调度优化问题,也正是这篇推文的主题。

作业车间调度问题可以描述为:一个车间中有mmm台功能不同的机器(machine),机器集合用I={1,2,⋯ ,m}I=\left\{1,2,\cdots,m\right\}I={1,2,,m}表示。有nnn个工件(job)需要加工,工件集合用J={1,2,⋯ ,n}J=\left\{1,2,\cdots,n\right\}J={1,2,,n}表示。每台机器不能同时处理两个及以上工件。每个工件需要以特定的顺序在特定的机器上进行加工,若工件jjj需要在机器mmm上加工,则称oijo_{ij}oij为一个作业(operation),作业集合用OOO表示,每个作业oijo_{ij}oij的加工时长为tijt_{ij}tij。特定的加工顺序意味着作业之间存在前后依赖关系,若一个工件的前继作业没有完成,该工件的后续作业就不能开始,且作业一旦开始就不能中断。作业车间调度问题的目标就是制定出一个满足所有约束的生产方案,使得所需的完工时间(makespan, 即所有工件加工完成所需要的时间)最小。

数学模型

参数及集合

I={1,2,⋯ ,m}I=\left\{1,2,\cdots,m\right\}I={1,2,,m}:机器集合

J={1,2,⋯ ,n}J=\left\{1,2,\cdots,n\right\}J={1,2,,n}:工件集合

OOO:作业集合

δj\delta_jδj:工件jjj的机器加工顺序

tijt_{ij}tij:作业oijo_{ij}oij的加工时间

变量

CmaxC_{max}Cmax:最大完工时间

sijs_{ij}sij:作业oijo_{ij}oij的开始时间

约束条件

  • 每个工序当前仅当被加工一次;
  • 工序的开始时间迟于前一道工序的完成时间;
  • 机器每次当且仅当能加工一个工件;
  • 每道工序的加工过程不可中断;

遗传算法求解作业车间调度问题

整体思路

  • 步骤1 初始化随机产生nPop个染色体个体,nPop为种群规模。
  • 步骤2 计算个体适应度,评价个体适应度值。
  • 步骤3 按赌轮选择策略选取下一代种群。
  • 步骤4a 按交叉概率Pc,对两父代个体交叉n次,从最优父代和所有后代中选择最优两染色体作为下一代。
  • 步骤4b 按变异概率Pm选择个体,进行变异操作生成新个体。
  • 步骤5 合并交叉、变异产生的新个体生成的新一代的种群
  • 步骤6 判断是否达到终止条件,若满足则输出最优解,结束算法;否则转步骤3。

编码与解码

采用基于工序编码全排列的编码方式进行编码,染色体的长度等于所有工件的工序之和。先将工序按照1,2,⋯ ,O1,2,\cdots,O1,2,,O的顺序编码,其中O代表总的工序数。编码顺序代表了工序被加工的优先级。

初始种群生成方式采用将工序编码随机全排列的方式随机生成,代码如下:

def generateRandomIndividual(population, job_number, machine_number):
    ind = list(range(job_number*machine_number))
    random.shuffle(ind)
    population.append([ind, None])

交叉

应用于作业车间调度问题方面,常见的遗传算法交叉算子包括单点交叉,多点交叉,均匀交叉,基于工件顺序的交叉和基于工件优先顺序的交叉等。

本文采用多点交叉方式,即随机产生两个交叉点位,交换两个点位之间的基因,代码如下:

def crossover(father, mother):
    indexes = range(len(father[0]))

    # 随机产生交叉点位
    start_index = random.choice(indexes)
    end_index = random.choice(indexes[start_index:])
    
    father_gen = father[0][start_index:end_index]
    fetus = removeFromList(mother[0], father_gen)
    result = []
    result.extend(fetus[:start_index])
    result.extend(father_gen)
    result.extend(fetus[start_index:])
    return [result, None]

变异

作业车间调度问题的遗传算法变异算子包括交换变异、插入变异和逆转变异等。

本文采用了交换变异的方式,即随机产生两个变异点,将两个变异点位的基因编码交换,以产生新的个体。

def mutation(population, mutation_rate):
    if(random.random() > mutation_rate):
        candidate = random.choice(population[1:])
        swap_rnd(candidate[0])
        candidate[1] = None
        
def swap_rnd(candidate):
    id1 = random.choice(range(len(candidate)))
    id2 = random.choice(range(len(candidate)))
    tmp = candidate[id1]
    candidate[id1] = candidate[id2]
    candidate[id2] = tmp
    return candidate

代码和结果展示

遗传算法主循环代码

def genetic(times, machines, n, population_number, iterations, rate):
    machine_number = len(machines[0])
    start_time = time.time()

    population = generate_population(population_number, n, machine_number)
    global_best_ind, global_best = sortAndGetBestIndividual(population, times, machines, n)
    
    for i in range(iterations):
        population = evolve(population, rate)
        best_ind, best_result = sortAndGetBestIndividual(population, times, machines, n)
        total_fitness, diffPercentage = getFitness(population)

        if(not global_best or best_result < global_best):
            global_best = best_result
            global_best_ind = copy.deepcopy(best_ind)

        printProgress(best_result, i, time.time() - start_time)
        checkDiversity(population, diffPercentage, n, machine_number)

    
    best_result, best_table = calculateMakespan(times, machines, global_best_ind[0], n)           
    print("\nOVERALL RESULT")
    print("RESULT: %s" %best_result)  # 目标函数值              
    print('the elapsed time:%ss'% (int(time.time() - start_time))) # 求解时间
    print("Permutation: ") 
    print(fromPermutation(global_best_ind[0], n)) # 输出最终解基因编码
    printTable(best_table)  # 输出结果
    plotResult(best_table, best_result) # 绘制甘特图

测试算例说明

第一行两个数分别代表工件数(n=10)和机器数(m=5)

每一行代表每个工件不同工序的加工时间,每个工序都用一对数字(x, y)来表示,其中x是处理该工序的机器编号,y是该工序的加工时间。

结果展示

甘特图

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eternal1995

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值