当前位置:   article > 正文

优化算法|遗传算法求解作业车间调度问题(Python)_python实现遗传算法车间调度求解的代码

python实现遗传算法车间调度求解的代码

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

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

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

前言

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

作业车间调度问题简介

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

作业车间调度问题可以描述为:一个车间中有 m m m台功能不同的机器(machine),机器集合用 I = { 1 , 2 , ⋯   , m } I=\left\{1,2,\cdots,m\right\} I={1,2,,m}表示。有 n n n个工件(job)需要加工,工件集合用 J = { 1 , 2 , ⋯   , n } J=\left\{1,2,\cdots,n\right\} J={1,2,,n}表示。每台机器不能同时处理两个及以上工件。每个工件需要以特定的顺序在特定的机器上进行加工,若工件 j j j需要在机器 m m m上加工,则称 o i j o_{ij} oij为一个作业(operation),作业集合用 O O O表示,每个作业 o i j o_{ij} oij的加工时长为 t i j t_{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}:工件集合

O O O:作业集合

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

t i j t_{ij} tij:作业 o i j o_{ij} oij的加工时间

变量

C m a x C_{max} Cmax:最大完工时间

s i j s_{ij} sij:作业 o i j o_{ij} oij的开始时间

约束条件

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

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

整体思路

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

编码与解码

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

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

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

交叉

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

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

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]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

变异

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

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

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

代码和结果展示

遗传算法主循环代码

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) # 绘制甘特图
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

测试算例说明

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

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

结果展示

甘特图

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/600221
推荐阅读
相关标签
  

闽ICP备14008679号