问题描述

柔性作业车间问题可描述为:每一待加工工件包含一道或一道以上工序,每个工件的工序 顺序都是已知的,每道工序可以在一台及以上的机器上完成加工, 加工的时间会因为所选择的加工机器不同而变得不同。 调度方案需要确定工序的加工顺序和机器的选择,从而使得整个调度系统的各指标达到最优。
加工过程需满足以下条件:
(1)工件工序只能在可加工设备上进行加工
(2)一台设备同一时间段只能对一个工件工序进行加工
(3)工件工序开始加工后无法停止
(4)工件下一工序的开始时间大于等于上一工序的结束时间,即工件工序具备加工次序

工件数据

仿真数据来源于文献:[1]高尚,李荣平.改进NSGA-Ⅱ求解多目标柔性作业车间调度问题[J].河北企业,2022(04):70-72.DOI:10.19885/j.cnki.hbqy.2022.04.021.

柔性车间调度优化算法python 柔性作业车间调度问题_i++

最早开始时间启发策略

最早开始时间启发策略是指在工件工序加工过程中,依据工件工序在各个设备上的最早的开始加工时间为依据将工件工序分配到设备进行加工。
实现代码:

/**
     * 启发策略-最早开始时间
     */
    private void EST(){
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) orderArr[j] = j;
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = 0;
                                    deviceIndex = orderArr[j];
                                    //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1;
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.startTime = deviceTime;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //System.out.println(i + "\t" + (workProcessTemp.get(i).get(0) - 1) + "\t" + deviceIndex);
                        shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时,结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) break;
        }
        printResult(workResult,endTime);
    }

启发求解结果:

柔性车间调度优化算法python 柔性作业车间调度问题_i++_02

最早开始时间启发策略改进首工序分配

在使用最早开始时间启发策略过程中,采用随机分配工序的方式到设备,并且一旦设备满足加工条件,就将工件分配到该设备进行加工。其结果也可以发现,总会存在个别设备未被分配加工任务,导致最大的工件完工时间较长。
在这里,采用匈牙利算法对首工序进行设备分配,其余工序同样采用随机分配的方式。当然,若是有兴趣的研究者可以逐次将下一工序进行分配。
实现代码:

/**
     * 贪婪启发策略-最早开始时间
     * 匈牙利算法分配第一工序和设备
     */
    private void GEST(){
        //第一个工序分配
        int[] assignResult=shopAssign(data.ProcessTime);
        //分配第一个工序
        for(int i=0;i<assignResult.length;i++){
            int deviceIndex = data.deviceId.length;
            double deviceTime = Double.MAX_VALUE;
            if (workProcess.get(i).size() != 0){
                if (workResult.get(assignResult[i]-1).size() == 0){
                    deviceTime = 0;
                    deviceIndex = assignResult[i]-1;
                }
                else{
                    deviceTime = workResult.get(assignResult[i]-1).get(workResult.get(assignResult[i]-1).size() - 1).endTime+1;
                    deviceIndex = assignResult[i]-1;
                }
            }
            if (deviceIndex != data.deviceId.length) {
                shop.workId = i + 1;
                shop.workProcess = workProcess.get(i).get(0);
                shop.deviceId = deviceIndex + 1;
                shop.startTime = deviceTime;
                startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                workResult.get(deviceIndex).add(shop.clone());
                workProcess.get(i).remove(0);
            }
        }
        //分配其余工序
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) orderArr[j] = j;
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = 0;
                                    deviceIndex = orderArr[j];
                                    //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1;
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1;
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.startTime = deviceTime;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //System.out.println(i + "\t" + (workProcessTemp.get(i).get(0) - 1) + "\t" + deviceIndex);
                        shop.endTime = shop.startTime + data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时,结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) break;
        }
        printResult(workResult,endTime);
    }

运行结果发现,工件在设备上的分配更为有序。

柔性车间调度优化算法python 柔性作业车间调度问题_i++_03

最早结束时间启发策略

与最早开始时间相同,最早结束时间启发策略是指在工件工序加工过程中,依据工件工序在各个设备上的最早的结束加工时间为依据将工件工序分配到设备进行加工。
实现代码:

/**
     * 启发策略-最早结束时间
     * 随机分配工序
     */
    private void EET(){
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) orderArr[j] = j;
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                    deviceIndex = orderArr[j];
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]] < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]]< deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.endTime = deviceTime;
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时,结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) break;
        }
        printResult(workResult,endTime);
    }

启发求解结果:

柔性车间调度优化算法python 柔性作业车间调度问题_i++_04

最早结束时间启发策略改进首工序分配

/**
     * 贪婪启发策略-最早结束时间
     * 匈牙利算法分配第一工序和设备
     */
    private void GEET(){
        //第一个工序分配
        int[] assignResult=shopAssign(data.ProcessTime);
        //分配第一个工序
        for(int i=0;i<assignResult.length;i++){
            int deviceIndex = data.deviceId.length;
            double deviceTime = Double.MAX_VALUE;
            if (workProcess.get(i).size() != 0){
                if (workResult.get(assignResult[i]-1).size() == 0){
                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][assignResult[i]-1];
                    deviceIndex = assignResult[i]-1;
                }
                else{
                    deviceTime = workResult.get(assignResult[i]-1).get(workResult.get(assignResult[i]-1).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][assignResult[i]-1];
                    deviceIndex = assignResult[i]-1;
                }
            }
            if (deviceIndex != data.deviceId.length) {
                shop.workId = i + 1;
                shop.workProcess = workProcess.get(i).get(0);
                shop.deviceId = deviceIndex + 1;
                shop.endTime = deviceTime;
                endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                workResult.get(deviceIndex).add(shop.clone());
                workProcess.get(i).remove(0);
            }
        }
        //分配其余工序
        while (true) {
            //遍历工件
            for (int i = 0; i < data.workId.length; i++) {
                //选取工序i的剩余第一个工序
                if (workProcess.get(i).size() != 0) {
                    //System.out.println("=======工件"+(i+1)+"|工序"+workProcess.get(i).get(0)+"=======");
                    //随机生成设备遍历顺序
                    int[] orderArr = new int[data.deviceId.length];
                    for (int j = 0; j < data.deviceId.length; j++) orderArr[j] = j;
                    shuffle(orderArr);
                    //找到开始时间最短的机器设备
                    int deviceIndex = data.deviceId.length;
                    double deviceTime = Double.MAX_VALUE;
                    //随机遍历机器
                    for (int j = 0; j < data.deviceId.length; j++) {
                        //工序只能在可加工设备上进行加工
                        if (data.ProcessTime[i][workProcess.get(i).get(0) - 1][orderArr[j]] != Double.MAX_VALUE) {
                            if (workResult.get(orderArr[j]).size() == 0) {
                                //下一工序的开始时间大于等于上一工序的结束时间
                                if (workProcess.get(i).get(0) == 1) {
                                    //第一个工序
                                    deviceTime = data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                    deviceIndex = orderArr[j];
                                    break;
                                }
                            } else {
                                if (workProcess.get(i).get(0) == 1) {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]] < deviceTime) {
                                        deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                        deviceIndex = orderArr[j];
                                    }
                                } else {
                                    if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]]< deviceTime) {
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime >= endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                        if (workResult.get(orderArr[j]).get(workResult.get(orderArr[j]).size() - 1).endTime < endTime[i][workProcess.get(i).get(0) - 2]) {
                                            deviceTime = endTime[i][workProcess.get(i).get(0) - 2]+1+data.ProcessTime[i][workProcess.get(i).get(0)-1][orderArr[j]];
                                            deviceIndex = orderArr[j];
                                        }
                                    }
                                }
                            }
                        }
                        //System.out.println("遍历设备"+(orderArr[j]+1)+"\t"+"目标设备"+(deviceIndex+1)+"\t"+deviceTime);
                    }
                    //更新工件信息
                    if (deviceIndex != data.deviceId.length) {
                        shop.workId = i + 1;
                        shop.workProcess = workProcess.get(i).get(0);
                        shop.deviceId = deviceIndex + 1;
                        shop.endTime = deviceTime;
                        endTime[i][workProcess.get(i).get(0) - 1] = shop.endTime;
                        shop.startTime = shop.endTime - data.ProcessTime[i][workProcess.get(i).get(0) - 1][deviceIndex];;
                        startTime[i][workProcess.get(i).get(0) - 1] = shop.startTime;
                        //工件信息加入结果列表
                        workResult.get(deviceIndex).add(shop.clone());
                        //删除该工序
                        workProcess.get(i).remove(0);
                    }
                }
            }
            //所有工序分配完毕时,结束
            boolean stopCondition = true;
            for (int i = 0; i < data.workId.length; i++) {
                if (workProcess.get(i).size() != 0) {
                    stopCondition = false;
                    break;
                }
            }
            if (stopCondition) break;
        }
        printResult(workResult,endTime);
    }

无论是最早开始时间还是最早结束时间策略,启发策略均能够得到可行的解方案。但是至于解方案的质量高低,还需要设计更好的启发策略。此次笔记仅实现了简单的两个启发策略,未来还有待进一步改善方法技术。

========================================
今天到此为止,后续记录其他应用问题的学习过程。