柔性作业车间调度问题-遗传算法求解方法

启发方法

柔性作业车间问题的启发求解方法如【柔性作业车间调度问题-启发求解方法】,主要使用了简单的最早开始加工时间和最早加工结束时间启发策略,虽然能够得到可行的加工方案,但是仍然存在可进一步改进的方向。

问题描述

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

工件数据

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

遗传算法求解步骤

(1)初始化种群-随机生成可行方案
(2)初始化种群最优适应值和最优个体-个体解码
(3)个体选择-轮盘赌概率选择策略
(4)个体交叉-POX交叉方式
(5)个体变异-交换变异
(6)子代生成-父代和变异种群竞争
(7)更新种群最佳适应值和最优个体

个体编码

采用工序、设备双层编码的方式分别用于解决工序排列及设备分配问题,编码过程如下:
在这里插入图片描述
以4工件(1:2;2:2;3:1;4:3)8个工序【1,1,2,2,3,4,4,4】为例,详细的工序编码过程如下:
(1)生成随机数组
在这里插入图片描述
(2)数组排序
在这里插入图片描述
(3)根据数组排序,选择工序并生成个体
在这里插入图片描述
(4)工序编码结果
个体基因位:表示待加工的工件编号
基因位所处位置:表示工件的加工顺序
工件次数:表示加工该工件的第XX个工序
根据(3)编码结果,首先加工工件4的第1道工序,紧接着工件4的第2道工序,…;整体加工顺序:4_1->4_2->3_1->1_1->2_1->4_3->2_2->1_2

假定各个工序可加工的设备编号如下:
1_1:1,3,4
1_2:3
2_1:2,4
2_2:1,3,4
3_1:1,2
4_1:1,2
4_2:3,4
4_3:2,3,4
根据工序编码结果,详细的设备编码过程是根据同基因位的工序的可用设备长度,随机生成[0,可用设备集合长度-1]之间的随机数,该随机数表示使用该工序可用设备集合中的索引位置,而不是设备编号;
例如对于1_1工序,随机数在[0,1,2]中产生。
实现代码如下:

//种群初始化-个体编码
        private void initPop(){
            for(int i=0;i<popNum;i++){
                int[] processRamdomIndex=processRandom();
                for(int j=0;j<processNum;j++){
                    processCode[i][j]=processList.get(processRamdomIndex[j]-1);
                }
                int[] deviceRandomIndex=deviceRandom(processCode[i]);
                for(int j=0;j<processNum;j++){
                    deviceCode[i][j]=deviceRandomIndex[j];
                }
            }
        }
    //产生个体长度的随机数-工序
        private int[] processRandom(){
            int[] processSort=new int[processNum];
            double[] processRandomValue=new double[processNum];
            Random random = new Random();
            for(int i=0;i<processNum;i++){
                processRandomValue[i]=random.nextDouble(10);
                processSort[i]=i+1;
            }
            //冒泡排序
            for(int i=1;i<processRandomValue.length;i++){
                for(int j=0;j<processRandomValue.length-i;j++){
                    if(processRandomValue[j]>processRandomValue[j+1]){
                        double temp=processRandomValue[j+1];
                        int tempIndex=processSort[j+1];
                        processRandomValue[j+1]=processRandomValue[j];
                        processSort[j+1]=processSort[j];
                        processRandomValue[j]=temp;
                        processSort[j]=tempIndex;
                    }
                }
            }
            return processSort;
        }
        //产生个体长度的随机数-设备索引
        private int[] deviceRandom(int[] processIndex){
            int[] deviceRandomValue=new int[processNum];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            int count=0;
            for(int i=0;i<processNum;i++){
                deviceRandomValue[count++]=random.nextInt(processDevice.get(processIndex[i]-1).get(workList[processIndex[i]-1]).size());
                workList[processIndex[i]-1]+=1;
            }
            return deviceRandomValue;
        }

个体解码

根据工序编码和设备编码,依据最早开始加工时间的贪婪策略,将工件工序分配到设备上。
实现代码如下:

//个体解码
        private double greedyDecode(int[] processPop,int[] devicePop,boolean rstType){
            //清空列表
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
            //记录开始和结束时间
            double[][] startTime=new double[data.workId.length][6];
            double[][] endTime=new double[data.workId.length][6];
            for(int i=0;i<startTime.length;i++){
                for(int j=0;j<6;j++){
                    startTime[i][j]=-1;
                    endTime[i][j]=-1;
                }
            }
            //记录工件使用次数-工序
            int[] workList=new int[data.workId.length];
            //分配工序到设备
            for(int i=0;i<processPop.length;i++){
                int deviceIndex=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).get(devicePop[i]);
                double deviceTime = Double.MAX_VALUE;
                if(workResult.get(deviceIndex-1).size()==0){
                    if(workList[processPop[i]-1]==0){
                        deviceTime=0;
                    }
                    else{
                        deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                    }
                }
                else{
                    //设备已存在分配的工序
                    //第一个工序分配
                    if(workList[processPop[i]-1]==0){
                        deviceTime=workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                    }
                    else{
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime >= endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                        }
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime < endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                        }
                    }
                }
                //更新工件信息
                shop.workId = processPop[i];
                shop.workProcess = workList[processPop[i]-1]+1;
                shop.deviceId = deviceIndex;
                shop.startTime = deviceTime;
                startTime[processPop[i]-1][workList[processPop[i]-1]] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[processPop[i]-1][workList[processPop[i]-1]][deviceIndex-1];
                endTime[processPop[i]-1][workList[processPop[i]-1]] = shop.endTime;
                workResult.get(deviceIndex-1).add(shop.clone());
                workList[processPop[i]-1]+=1;

            }
            //计算最大完工时间
            double maxTime=maxEndTime(endTime);
            if(rstType)printResult(workResult,endTime);
            return maxTime;
        }
        //计算最大完工时间
        private double maxEndTime(double[][] endTime){
            double maxTime=0;
            double[] endTimeList=new double[data.workId.length];
            for(int i=0;i<endTime.length;i++){
                endTimeList[i]=getMax(endTime[i]);
            }
            maxTime=getMax(endTimeList);
            return maxTime;
        }

适应值计算

//计算个体适应值
        private double fitnessPop(int[] processPop,int[] devicePop,boolean rstType){
            double fitness=0;
            fitness=greedyDecode(processPop,devicePop,rstType);
            return fitness;
        }

初始最优个体

//初始最优个体与适应值
        private void initFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }

个体变异

变异操作一方面是对个体施加轻微的扰动,有利于进行交叉;一方面是对交叉个体实施较大的扰动,以便于在更大的范围内产生新个体并使得个体跳出局部收敛状态。在进行变异操作时,若mutateType=true表示进行扰动,期间只对个体的两个基因位进行交换;否则表示进行常规的变异操作,变异的位数为个体长度的0.2。

在实施变异操作过程中,变异位置为偶数个随机生成的[0,个体长度-1]的随机数。

针对个体的工序,互换第i和i+1个基因位上的工件编号,直到完成所有基因位的变异(i=i+2)。

针对个体的设备,根据变异后的个体工序编码,随机产生相应的可用设备索引。
在这里插入图片描述

实现代码如下:

//个体变异:工序随机交换指定偶数位;设备随机选取
        private int[][] mutationPop(int[] processPop,int[] devicePop,boolean mutateType){
            int[][] newPop=new int[2][processPop.length];
            newPop[0]=processPop.clone();
            newPop[1]=devicePop.clone();
            Random random = new Random();
            int mutateNum;
            int[] processIndex;
            //交叉前变异
            if(mutateType){
                mutateNum=2;
                processIndex=new int[mutateNum];
            }
            //交叉后变异
            else{
                //变异位数
                mutateNum=processPop.length/5;
                if(mutateNum%2!=0)mutateNum-=1;
                processIndex=new int[mutateNum];
            }
            //工序变异位置
            for(int i=0;i<processIndex.length;i++){
                processIndex[i]=random.nextInt(newPop[0].length);
            }
            //工序变异
            for(int i=0;i<processIndex.length;i+=2){
                int temp=newPop[0][processIndex[i]];
                newPop[0][processIndex[i]]=newPop[0][processIndex[i+1]];
                newPop[0][processIndex[i+1]]=temp;
            }
            //设备变异位置
            for(int i=0;i<processIndex.length;i++){
                //确定工序
                int count=0;
                for(int j=0;j<newPop[0].length;j++){
                    if(newPop[0][j]==newPop[0][processIndex[i]]){
                        newPop[1][j]=random.nextInt(processDevice.get(newPop[0][processIndex[i]]-1).get(count).size());
                        count+=1;
                    }
                }
            }
            return newPop;
        }

个体交叉

交叉操作是对个体信息进行交换,有利于产生更好的个体。在进行交叉时,采用POX交叉方式,如下:
在这里插入图片描述
红色位置表示个体对应的交叉位置-交叉点,需满足交叉点在个体1和个体2的工件编号中保持一致;例如上述个体1的交叉点为:4,1,4;而个体2的交叉点为:4,4,1。区别仅是位置不同。
(1)以个体1的交叉点位为基础,将个体2的非交叉点位的基因填充到个体1
(2)以个体2的交叉点位为基础,将个体1的非交叉点位的基因填充到个体2
在这里插入图片描述
实现代码如下:

//轮盘赌策略-计算选择概率
        private double[] rouletteStrategy(int[][] processCodeChild,int[][] deviceCodeChild){
            double[] selectedProb=new double[popNum];
            double fitnessSum=0;
            //计算个体适应值
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessPop(processCodeChild[i],deviceCodeChild[i],false);
                fitnessSum+=selectedProb[i];
            }
            //反差
            double selectedSum=0;
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessSum-selectedProb[i];
                selectedSum+=selectedProb[i];
            }
            //计算累积概率
            double[] selectedProbTemp=selectedProb.clone();
            for(int i=0;i<popNum;i++){
                if(i==0)selectedProb[i]=selectedProbTemp[i]/selectedSum;
                else selectedProb[i]=(selectedProbTemp[i]/selectedSum)+selectedProb[i-1];
            }
            return selectedProb;
        }
        //个体选择
        private int[] selectPop(double[] selectedProb){
            int[] selectedIndex=new int[2];
            Random random = new Random();
            for(int i=0;i<selectedIndex.length;i++){
                double temp=random.nextDouble(0,1);
                //找到位置索引
                for(int j=0;j<selectedProb.length;j++){
                    if(temp<=selectedProb[j]){
                        selectedIndex[i]=j;
                        break;
                    }
                }
            }
            return selectedIndex;
        }
        //个体交叉
        private int[][] crossPop(int[] selectedIndex){
            int[][] crossResult=new int[4][processNum];
            //随机交叉位置
            int crossNum=processNum/5;
            if(crossNum<=0)crossNum=1;
            int[] crossIndex=new int[crossNum];
            int[] arrTemp=new int[processNum];
            for(int i=0;i<arrTemp.length;i++){
                arrTemp[i]=i;
            }
            shuffle(arrTemp);
            for(int i=0;i<crossIndex.length;i++){
                crossIndex[i]=arrTemp[i];
            }
            //找到另一个体上的交叉位置
            int[] anotherCrossIndex=new int[crossNum];
            for(int i=0;i<anotherCrossIndex.length;i++){anotherCrossIndex[i]=-1;}
            for(int i=0;i<crossIndex.length;i++){
                for(int j=0;j<processCode[selectedIndex[1]].length;j++){
                    if(processCode[selectedIndex[1]][j]==processCode[selectedIndex[0]][crossIndex[i]]){
                        //是否已经添加过
                        boolean appendType=false;
                        for(int k=0;k<anotherCrossIndex.length;k++){
                            if(anotherCrossIndex[k]==j){
                                appendType=true;
                                break;
                            }
                        }
                        if(!appendType){
                            anotherCrossIndex[i]=j;
                        }
                    }
                }
            }
            //交叉
            for(int i=0;i<selectedIndex.length;i++){
                int count=0;
                for(int j=0;j<processNum;j++){
                    if(i==0){
                        if(judgeExist(crossIndex,j)){
                            crossResult[0][j]=processCode[selectedIndex[i]][j];
                            crossResult[2][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(anotherCrossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[0][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[2][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                    else{
                        if(judgeExist(anotherCrossIndex,j)){
                            crossResult[1][j]=processCode[selectedIndex[i]][j];
                            crossResult[3][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(crossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[1][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[3][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                }
            }
            //校验设备分配-如果超出索引,则随机分配;
            crossResult[2]=deviceCheck(crossResult[0].clone(),crossResult[2].clone());
            crossResult[3]=deviceCheck(crossResult[1].clone(),crossResult[3].clone());
            return crossResult;
        }

设备校验

根据交叉结果,由于工件位置发生变化,原位置处代表的工序在交叉后的个体位置代表的工序有所不同,导致部分工件工序的可用设备出现异常,即使用不可用设备对工件工序进行了加工,在这种情况下,需要对个体的设备进行校验。与交叉操作相同,变异操作同样也需要进行设备校验。
在这里插入图片描述

设备校验的原则是依次检验个体的工序和设备编码,若该设备编码不满足对应位置的工件工序,则对该工件工序随机产生一个新的可用设备索引。
实现代码如下:

//设备校验
        private int[] deviceCheck(int[] processPop,int[] devicePop){
            int[] checkResult=new int[devicePop.length];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            for(int i=0;i<devicePop.length;i++){
                int deviceIndexLength=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).size();
                if(devicePop[i]<deviceIndexLength){
                    checkResult[i]=devicePop[i];
                }
                else{
                    checkResult[i]=random.nextInt(deviceIndexLength);
                }
                workList[processPop[i]-1]+=1;
            }
            return checkResult;
        }

子代生成

通过父代种群和变异种群,选择前N个适应值最小的个体组成新的迭代种群。
实现代码如下:

//子代生成
        private void generateChild(int[][] processCode,int[][] deviceCode,int[][] processCodeChild,int[][] deviceCodeChild){
            //计算原种群适应值
            double[] targetValue=new double[processCode.length+processCodeChild.length];
            int[] targetIndex=new int[processCode.length+processCodeChild.length];
            for(int i=0;i<processCode.length;i++){
               targetValue[i]=fitnessPop(processCode[i].clone(),deviceCode[i].clone(),false);
                targetIndex[i]=i;
            }
            for(int i=0;i<processCodeChild.length;i++){
                targetValue[i+processCode.length]=fitnessPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                targetIndex[i+processCode.length]=i+processCode.length;
            }
            //排序
            for(int i=1;i<targetValue.length;i++){
                for(int j=0;j<targetValue.length-i;j++){
                    if(targetValue[j]>targetValue[j+1]){
                        double temp=targetValue[j+1];
                        int tempIndex=targetIndex[j+1];
                        targetValue[j+1]=targetValue[j];
                        targetIndex[j+1]=targetIndex[j];
                        targetValue[j]=temp;
                        targetIndex[j]=tempIndex;
                    }
                }
            }
            //生成子代
            for(int i=0;i<this.processCode.length;i++){
                if(targetIndex[i]<processCode.length){
                    this.processCode[i]=processCode[targetIndex[i]].clone();
                    this.deviceCode[i]=deviceCode[targetIndex[i]].clone();
                }
                else{
                    this.processCode[i]=processCodeChild[targetIndex[i]-processCode.length].clone();
                    this.deviceCode[i]=deviceCodeChild[targetIndex[i]-processCode.length].clone();
                }
            }
        }

更新最优个体

//更新最优个体与适应值
        private void updateFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }

算法流程

private void GAMethod(){
            //初始化
            initPop();
            //初始最优个体与适应值
            initFitness();
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            //迭代记录
            int iter=0;
            while(iter<ITER){
                //扰动
                int[][] processCodeTemp=processCode.clone();
                int[][] deviceCodeTemp=deviceCode.clone();
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCode[i].clone(),deviceCode[i].clone(),true);
                    processCodeTemp[i]=mutatePopTemp[0].clone();
                    deviceCodeTemp[i]=mutatePopTemp[1].clone();
                }
                //交叉
                int[][] processCodeChild=processCode.clone();
                int[][] deviceCodeChild=deviceCode.clone();
                double[] selectedProb=rouletteStrategy(processCodeTemp,deviceCodeTemp);
                int crossCount=0;
                for(int i=0;i<processCode.length/2;i++){
                    int[] selectedIndex= selectPop(selectedProb);
                    int[][] crossResult=crossPop(selectedIndex);
                    processCodeChild[crossCount]=crossResult[0].clone();
                    deviceCodeChild[crossCount]=crossResult[2].clone();
                    crossCount++;
                    processCodeChild[crossCount]=crossResult[1].clone();
                    deviceCodeChild[crossCount]=crossResult[3].clone();
                    crossCount++;
                }
                //变异
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                    processCodeChild[i]=mutatePopTemp[0].clone();
                    deviceCodeChild[i]=mutatePopTemp[1].clone();
                }
                //选择
                generateChild(processCode.clone(),deviceCode.clone(),processCodeChild.clone(),deviceCodeChild.clone());
                //更新
                updateFitness();
                iter++;
            }
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            System.out.println();
            greedyDecode(processBest,deviceBest,true);
        }

求解结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

结果对比

代码实现方案结果:
在这里插入图片描述
所得方案的各个工件的最大完工时间为67,且各个工件的完工时间均满足交货期限制,延期时间为0。
[1]高尚,李荣平.改进NSGA-Ⅱ求解多目标柔性作业车间调度问题[J]论文结果的最大完工时间为69,延期时间为0。

改进方向

(1)多目标求解-非支配方法
(2)变异和交叉概率-自适应变异概率,交叉概率
(3)邻域搜索策略
(4)混合智能算法策略

完整代码

由于代码使用涉及【柔性作业车间调度问题-启发求解方法】部分函数,可以在该博客进行查询或者联系作者进行交流,在这里仅给出遗传算法的求解代码。

//遗传算法求解
    GAAlgorithm ga=new GAAlgorithm();
    class GAAlgorithm{
        Data data;
        //工件
        shopStructure shop;
        //种群个数
        int popNum;
        //工序个数
        int processNum;
        //迭代次数
        int ITER;
        //工序列表
        ArrayList<Integer>processList;
        //工序编码
        int[][] processCode;
        //设备编码
        int[][] deviceCode;
        //工序可用设备编码
        ArrayList<ArrayList<ArrayList<Integer>>> processDevice;
        //最优工序个体
        int[] processBest;
        //最优设备个体
        int[] deviceBest;
        //最优目标值
        double targetBest;
        //调度结果
        ArrayList<ArrayList<shopStructure>>workResult;
        private GAAlgorithm(){
            data=new Data();
            //初始化工件类
            shop=new shopStructure();
            //种群个数
            popNum=200;
            processNum=0;
            ITER=500;
            for(int i=0;i<data.workProcess.length;i++){
                processNum+=data.workProcess[i].length;
            }
            //工序编码
            processCode=new int[popNum][processNum];
            processList=new ArrayList<>();
            for(int i=0;i<data.workProcess.length;i++){
                for(int j=0;j<data.workProcess[i].length;j++){
                    processList.add(i+1);
                }
            }
            //设备编码
            deviceCode=new int[popNum][processNum];
            //工序可用设备编码
            processDevice=new ArrayList<>();
            for(int i=0;i<data.ProcessTime.length;i++){
                ArrayList<ArrayList<Integer>>processDevice1=new ArrayList<>();
                for(int j=0;j<data.ProcessTime[i].length;j++){
                    double[] deviceTimeList=new double[data.deviceId.length];
                    int[] deviceTimeIndex=new int[data.deviceId.length];
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        deviceTimeList[k]=data.ProcessTime[i][j][k];
                        deviceTimeIndex[k]=k;
                    }
                    //由小到大排序
                    for(int ii=1;ii<deviceTimeList.length;ii++){
                        for(int jj=0;jj<deviceTimeList.length-ii;jj++){
                            if(deviceTimeList[jj]>deviceTimeList[jj+1]){
                                double temp=deviceTimeList[jj+1];
                                int tempIndex=deviceTimeIndex[jj+1];
                                deviceTimeList[jj+1]=deviceTimeList[jj];
                                deviceTimeIndex[jj+1]=deviceTimeIndex[jj];
                                deviceTimeList[jj]=temp;
                                deviceTimeIndex[jj]=tempIndex;
                            }
                        }
                    }
                    ArrayList<Integer>processDevice2=new ArrayList<>();
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        if(deviceTimeList[k]!=Double.MAX_VALUE){
                            processDevice2.add(deviceTimeIndex[k]+1);
                        }
                    }
                    processDevice1.add(processDevice2);
                }
                processDevice.add(processDevice1);
            }
            //最优工序个体
            processBest=new int[processNum];
            //最优设备个体
            deviceBest=new int[processNum];
            //最优目标值
            targetBest=Double.MAX_VALUE;
            //初始化调度结果
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
        }
        //种群初始化-个体编码
        private void initPop(){
            for(int i=0;i<popNum;i++){
                int[] processRamdomIndex=processRandom();
                for(int j=0;j<processNum;j++){
                    processCode[i][j]=processList.get(processRamdomIndex[j]-1);
                }
                int[] deviceRandomIndex=deviceRandom(processCode[i]);
                for(int j=0;j<processNum;j++){
                    deviceCode[i][j]=deviceRandomIndex[j];
                }
            }
        }
        //初始最优个体与适应值
        private void initFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //个体变异:工序随机交换指定偶数位;设备随机选取
        private int[][] mutationPop(int[] processPop,int[] devicePop,boolean mutateType){
            int[][] newPop=new int[2][processPop.length];
            newPop[0]=processPop.clone();
            newPop[1]=devicePop.clone();
            Random random = new Random();
            int mutateNum;
            int[] processIndex;
            //交叉前变异
            if(mutateType){
                mutateNum=2;
                processIndex=new int[mutateNum];
            }
            //交叉后变异
            else{
                //变异位数
                mutateNum=processPop.length/5;
                if(mutateNum%2!=0)mutateNum-=1;
                processIndex=new int[mutateNum];
            }
            //工序变异位置
            for(int i=0;i<processIndex.length;i++){
                processIndex[i]=random.nextInt(newPop[0].length);
            }
//            System.out.println();
//            System.out.println("工序变异位置");
//            for(int i=0;i<processIndex.length;i++){
//                System.out.print((processIndex[i]+1)+"\t");
//            }
//            System.out.println();
            //工序变异
            for(int i=0;i<processIndex.length;i+=2){
                int temp=newPop[0][processIndex[i]];
                newPop[0][processIndex[i]]=newPop[0][processIndex[i+1]];
                newPop[0][processIndex[i+1]]=temp;
            }
//            System.out.println("工序变异结果");
//            for(int j=0;j<newPop[0].length;j++){
//                System.out.print(newPop[0][j]+"\t");
//            }
//            System.out.println();
            //设备变异位置
            for(int i=0;i<processIndex.length;i++){
                //确定工序
                int count=0;
                for(int j=0;j<newPop[0].length;j++){
                    if(newPop[0][j]==newPop[0][processIndex[i]]){
                        newPop[1][j]=random.nextInt(processDevice.get(newPop[0][processIndex[i]]-1).get(count).size());
                        count+=1;
                    }
                }
            }
//            System.out.println("设备变异结果");
//            for(int i=0;i<newPop[1].length;i++){
//                System.out.print(newPop[1][i]+"\t");
//            }
            return newPop;
        }
        //轮盘赌策略-计算选择概率
        private double[] rouletteStrategy(int[][] processCodeChild,int[][] deviceCodeChild){
            double[] selectedProb=new double[popNum];
            double fitnessSum=0;
            //计算个体适应值
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessPop(processCodeChild[i],deviceCodeChild[i],false);
                fitnessSum+=selectedProb[i];
            }
            //反差
            double selectedSum=0;
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessSum-selectedProb[i];
                selectedSum+=selectedProb[i];
            }
            //计算累积概率
            double[] selectedProbTemp=selectedProb.clone();
            for(int i=0;i<popNum;i++){
                if(i==0)selectedProb[i]=selectedProbTemp[i]/selectedSum;
                else selectedProb[i]=(selectedProbTemp[i]/selectedSum)+selectedProb[i-1];
            }
            return selectedProb;
        }
        //个体选择
        private int[] selectPop(double[] selectedProb){
            int[] selectedIndex=new int[2];
            Random random = new Random();
            for(int i=0;i<selectedIndex.length;i++){
                double temp=random.nextDouble(0,1);
                //找到位置索引
                for(int j=0;j<selectedProb.length;j++){
                    if(temp<=selectedProb[j]){
                        selectedIndex[i]=j;
                        break;
                    }
                }
            }
            return selectedIndex;
        }
        //个体交叉
        private int[][] crossPop(int[] selectedIndex){
            int[][] crossResult=new int[4][processNum];
            //随机交叉位置
            int crossNum=processNum/5;
            if(crossNum<=0)crossNum=1;
            int[] crossIndex=new int[crossNum];
            int[] arrTemp=new int[processNum];
            for(int i=0;i<arrTemp.length;i++){
                arrTemp[i]=i;
            }
            shuffle(arrTemp);
            for(int i=0;i<crossIndex.length;i++){
                crossIndex[i]=arrTemp[i];
            }
//            System.out.println("个体1交叉位置:");
//            for(int i=0;i<crossIndex.length;i++){
//                System.out.print(crossIndex[i]+"\t");
//            }
//            System.out.println();
            //找到另一个体上的交叉位置
            int[] anotherCrossIndex=new int[crossNum];
            for(int i=0;i<anotherCrossIndex.length;i++){anotherCrossIndex[i]=-1;}
            for(int i=0;i<crossIndex.length;i++){
                for(int j=0;j<processCode[selectedIndex[1]].length;j++){
                    if(processCode[selectedIndex[1]][j]==processCode[selectedIndex[0]][crossIndex[i]]){
                        //是否已经添加过
                        boolean appendType=false;
                        for(int k=0;k<anotherCrossIndex.length;k++){
                            if(anotherCrossIndex[k]==j){
                                appendType=true;
                                break;
                            }
                        }
                        if(!appendType){
                            anotherCrossIndex[i]=j;
                        }
                    }
                }
            }
//            System.out.println("个体2交叉位置:");
//            for(int i=0;i<anotherCrossIndex.length;i++){
//                System.out.print(anotherCrossIndex[i]+"\t");
//            }
//            System.out.println();
            //交叉
            for(int i=0;i<selectedIndex.length;i++){
                int count=0;
                for(int j=0;j<processNum;j++){
                    if(i==0){
                        if(judgeExist(crossIndex,j)){
                            crossResult[0][j]=processCode[selectedIndex[i]][j];
                            crossResult[2][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(anotherCrossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[0][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[2][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                    else{
                        if(judgeExist(anotherCrossIndex,j)){
                            crossResult[1][j]=processCode[selectedIndex[i]][j];
                            crossResult[3][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(crossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[1][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[3][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                }
            }
            //校验设备分配-如果超出索引,则随机分配;
            crossResult[2]=deviceCheck(crossResult[0].clone(),crossResult[2].clone());
            crossResult[3]=deviceCheck(crossResult[1].clone(),crossResult[3].clone());
            return crossResult;
        }
        //子代生成
        private void generateChild(int[][] processCode,int[][] deviceCode,int[][] processCodeChild,int[][] deviceCodeChild){
            //计算原种群适应值
            double[] targetValue=new double[processCode.length+processCodeChild.length];
            int[] targetIndex=new int[processCode.length+processCodeChild.length];
            for(int i=0;i<processCode.length;i++){
                targetValue[i]=fitnessPop(processCode[i].clone(),deviceCode[i].clone(),false);
                targetIndex[i]=i;
            }
            for(int i=0;i<processCodeChild.length;i++){
                targetValue[i+processCode.length]=fitnessPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                targetIndex[i+processCode.length]=i+processCode.length;
            }
            //排序
            for(int i=1;i<targetValue.length;i++){
                for(int j=0;j<targetValue.length-i;j++){
                    if(targetValue[j]>targetValue[j+1]){
                        double temp=targetValue[j+1];
                        int tempIndex=targetIndex[j+1];
                        targetValue[j+1]=targetValue[j];
                        targetIndex[j+1]=targetIndex[j];
                        targetValue[j]=temp;
                        targetIndex[j]=tempIndex;
                    }
                }
            }
            //生成子代
            for(int i=0;i<this.processCode.length;i++){
                if(targetIndex[i]<processCode.length){
                    this.processCode[i]=processCode[targetIndex[i]].clone();
                    this.deviceCode[i]=deviceCode[targetIndex[i]].clone();
                }
                else{
                    this.processCode[i]=processCodeChild[targetIndex[i]-processCode.length].clone();
                    this.deviceCode[i]=deviceCodeChild[targetIndex[i]-processCode.length].clone();
                }
            }
        }
        //更新最优个体与适应值
        private void updateFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //判断是否存在
        private boolean judgeExist(int[] arr,int element){
            boolean existType=false;
            for(int i=0;i<arr.length;i++){
                if(arr[i]==element){
                    existType=true;
                    break;
                }
            }
            return existType;
        }
        //计算个体适应值
        private double fitnessPop(int[] processPop,int[] devicePop,boolean rstType){
            double fitness=0;
            fitness=greedyDecode(processPop,devicePop,rstType);
            return fitness;
        }
        //个体解码
        private double greedyDecode(int[] processPop,int[] devicePop,boolean rstType){
            //清空列表
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
//            System.out.println("解码个体");
//            for(int i=0;i<processPop.length;i++){
//                System.out.print(processPop[i]+"\t");
//            }
//            System.out.println();
//            for(int i=0;i<devicePop.length;i++){
//                System.out.print(devicePop[i]+"\t");
//            }
//            System.out.println();
            //记录开始和结束时间
            double[][] startTime=new double[data.workId.length][6];
            double[][] endTime=new double[data.workId.length][6];
            for(int i=0;i<startTime.length;i++){
                for(int j=0;j<6;j++){
                    startTime[i][j]=-1;
                    endTime[i][j]=-1;
                }
            }
            //记录工件使用次数-工序
            int[] workList=new int[data.workId.length];
            //分配工序到设备
            for(int i=0;i<processPop.length;i++){
//                System.out.println("工序:");
//                for(int ii=0;ii<workList.length;ii++){
//                    System.out.print(workList[ii]+"\t");
//                }
//                System.out.println();
                int deviceIndex=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).get(devicePop[i]);
                double deviceTime = Double.MAX_VALUE;
                if(workResult.get(deviceIndex-1).size()==0){
                    if(workList[processPop[i]-1]==0){
                        deviceTime=0;
                    }
                    else{
                        deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                    }
                }
                else{
                    //设备已存在分配的工序
                    //第一个工序分配
                    if(workList[processPop[i]-1]==0){
                        deviceTime=workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                    }
                    else{
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime >= endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                        }
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime < endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                        }
                    }
                }
                //更新工件信息
                shop.workId = processPop[i];
                shop.workProcess = workList[processPop[i]-1]+1;
                shop.deviceId = deviceIndex;
                shop.startTime = deviceTime;
                startTime[processPop[i]-1][workList[processPop[i]-1]] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[processPop[i]-1][workList[processPop[i]-1]][deviceIndex-1];
                endTime[processPop[i]-1][workList[processPop[i]-1]] = shop.endTime;
                workResult.get(deviceIndex-1).add(shop.clone());
                workList[processPop[i]-1]+=1;

            }
            //计算最大完工时间
            double maxTime=maxEndTime(endTime);
            if(rstType)printResult(workResult,endTime);
            return maxTime;
        }
        //计算最大完工时间
        private double maxEndTime(double[][] endTime){
            double maxTime=0;
            double[] endTimeList=new double[data.workId.length];
            for(int i=0;i<endTime.length;i++){
                endTimeList[i]=getMax(endTime[i]);
            }
            maxTime=getMax(endTimeList);
            return maxTime;
        }
        //产生个体长度的随机数-工序
        private int[] processRandom(){
            int[] processSort=new int[processNum];
            double[] processRandomValue=new double[processNum];
            Random random = new Random();
            for(int i=0;i<processNum;i++){
                processRandomValue[i]=random.nextDouble(10);
                processSort[i]=i+1;
            }
            //冒泡排序
            for(int i=1;i<processRandomValue.length;i++){
                for(int j=0;j<processRandomValue.length-i;j++){
                    if(processRandomValue[j]>processRandomValue[j+1]){
                        double temp=processRandomValue[j+1];
                        int tempIndex=processSort[j+1];
                        processRandomValue[j+1]=processRandomValue[j];
                        processSort[j+1]=processSort[j];
                        processRandomValue[j]=temp;
                        processSort[j]=tempIndex;
                    }
                }
            }
            return processSort;
        }
        //产生个体长度的随机数-设备索引
        private int[] deviceRandom(int[] processIndex){
            int[] deviceRandomValue=new int[processNum];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            int count=0;
            for(int i=0;i<processNum;i++){
                deviceRandomValue[count++]=random.nextInt(processDevice.get(processIndex[i]-1).get(workList[processIndex[i]-1]).size());
                workList[processIndex[i]-1]+=1;
            }
            return deviceRandomValue;
        }
        //设备校验
        private int[] deviceCheck(int[] processPop,int[] devicePop){
            int[] checkResult=new int[devicePop.length];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            for(int i=0;i<devicePop.length;i++){
                int deviceIndexLength=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).size();
                if(devicePop[i]<deviceIndexLength){
                    checkResult[i]=devicePop[i];
                }
                else{
                    checkResult[i]=random.nextInt(deviceIndexLength);
                }
                workList[processPop[i]-1]+=1;
            }
            return checkResult;
        }
        private void GAMethod(){
            //初始化
            initPop();
//            System.out.println("初始种群:");
//            for(int i=0;i<popNum;i++){
//                System.out.println("个体"+(i+1)+"工序编码");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(processCode[i][j]+"\t");
//                }
//                System.out.println();
//                System.out.println("个体"+(i+1)+"设备编码索引");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(deviceCode[i][j]+"\t");
//                }
//                System.out.println();
//            }
            //初始最优个体与适应值
            initFitness();
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            //迭代记录
            int iter=0;
            while(iter<ITER){
//                System.out.println();
//                System.out.println("=========第"+(iter+1)+"次迭代=========");
//                System.out.println("最优值:"+targetBest);
                //扰动
                int[][] processCodeTemp=processCode.clone();
                int[][] deviceCodeTemp=deviceCode.clone();
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCode[i].clone(),deviceCode[i].clone(),true);
                    processCodeTemp[i]=mutatePopTemp[0].clone();
                    deviceCodeTemp[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("扰动种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //交叉
                int[][] processCodeChild=processCode.clone();
                int[][] deviceCodeChild=deviceCode.clone();
                double[] selectedProb=rouletteStrategy(processCodeTemp,deviceCodeTemp);
                int crossCount=0;
                for(int i=0;i<processCode.length/2;i++){
                    int[] selectedIndex= selectPop(selectedProb);
                    int[][] crossResult=crossPop(selectedIndex);
                    processCodeChild[crossCount]=crossResult[0].clone();
                    deviceCodeChild[crossCount]=crossResult[2].clone();
                    crossCount++;
                    processCodeChild[crossCount]=crossResult[1].clone();
                    deviceCodeChild[crossCount]=crossResult[3].clone();
                    crossCount++;
                }
//                System.out.println();
//                System.out.println("交叉种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //变异
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                    processCodeChild[i]=mutatePopTemp[0].clone();
                    deviceCodeChild[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("变异种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //选择
                generateChild(processCode.clone(),deviceCode.clone(),processCodeChild.clone(),deviceCodeChild.clone());
//                System.out.println("子代种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //更新
                updateFitness();
                iter++;
            }
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            System.out.println();
            greedyDecode(processBest,deviceBest,true);
        }
    }

完整代码(启发+遗传方法)

import ilog.concert.IloException;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;

import java.util.ArrayList;
import java.util.Random;
//启发方法求解,遗传算法求解,cplex求解
public class FlexibleShopSchedulingProblemDemo {
    //实验数据
    class Data{
        //工件编号
        int[] workId={1,2,3,4,5,6,7,8};
        //工序
        int[][] workProcess={
                {1,2,3},
                {1,2,3,4,5,6},
                {1,2,3,4},
                {1,2,3},
                {1,2},
                {1,2,3},
                {1,2,3,4},
                {1,2,3}
        };
        //工件交货期
        double[] deliveryDate={60,100,120,80,60,80,Double.MAX_VALUE,100};
        //设备编号
        int[] deviceId={1,2,3,4,5,6,7,8};
        //加工时间
        double[][][] ProcessTime={
                //工件1
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,12,Double.MAX_VALUE,10,9,Double.MAX_VALUE},
                        {17,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,10,Double.MAX_VALUE,15},
                        {Double.MAX_VALUE,24,Double.MAX_VALUE,11,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE}
                },
                //工件2
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,11,10,21,14,Double.MAX_VALUE,17},
                        {8,12,Double.MAX_VALUE,Double.MAX_VALUE,19,11,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,15,Double.MAX_VALUE,21,25,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,9,Double.MAX_VALUE,},
                        {12,15,Double.MAX_VALUE,14,9,Double.MAX_VALUE,10,Double.MAX_VALUE},
                        {Double.MAX_VALUE,9,Double.MAX_VALUE,7,10,8,Double.MAX_VALUE,Double.MAX_VALUE}
                },
                //工件3
                {
                        {Double.MAX_VALUE,14,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,Double.MAX_VALUE},
                        {Double.MAX_VALUE,23,23,17,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,20,9,22,Double.MAX_VALUE,Double.MAX_VALUE,21},
                        {7,Double.MAX_VALUE,10,Double.MAX_VALUE,8,11,9,Double.MAX_VALUE}
                },
                //工件4
                {
                        {Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,17,18,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE,Double.MAX_VALUE,12,Double.MAX_VALUE},
                        {Double.MAX_VALUE,8,11,8,Double.MAX_VALUE,Double.MAX_VALUE,9,20}
                },
                //工件5
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,24,10,16,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,19}
                },
                //工件6
                {
                        {Double.MAX_VALUE,20,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,22,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,19,Double.MAX_VALUE},
                        {19,Double.MAX_VALUE,17,16,16,Double.MAX_VALUE,Double.MAX_VALUE,18}
                },
                //工件7
                {
                        {Double.MAX_VALUE,Double.MAX_VALUE,16,Double.MAX_VALUE,17,16,Double.MAX_VALUE,Double.MAX_VALUE},
                        {22,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,20,22,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,18,23,Double.MAX_VALUE,Double.MAX_VALUE},
                        {Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,10,Double.MAX_VALUE,24}
                },
                //工件8
                {
                        {11,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,21,21},
                        {25,11,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE},
                        {24,18,Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE,25,Double.MAX_VALUE,20}
                }
        };

    }
    //作业结构类
    class shopStructure implements Cloneable{
        //工件编号
        int workId;
        //工序编号
        int workProcess;
        //设备编号
        int deviceId;
        //开始时间
        double startTime;
        //结束时间
        double endTime;
        //拷贝方法
        @Override
        public shopStructure clone() {
            shopStructure shop = null;
            try {
                shop = (shopStructure) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return shop;
        }
    }
    //定义类属性
    //数据
    Data data;
    //工件
    shopStructure shop;
    //工序
    ArrayList<ArrayList<Integer>>workProcess;
    //调度结果
    ArrayList<ArrayList<shopStructure>>workResult;
    //记录工序的开始时间
    double[][] startTime;
    //记录工序的结束时间
    double[][] endTime;
    private FlexibleShopSchedulingProblemDemo(){
        //初始化实验数据
        data=new Data();
        //初始化工件类
        shop=new shopStructure();
        //初始化工序
        workProcess=new ArrayList<ArrayList<Integer>>();
        for(int i=0;i<data.workProcess.length;i++){
            ArrayList<Integer>workProcessTemp=new ArrayList<Integer>();
            for(int j=0;j<data.workProcess[i].length;j++){
                workProcessTemp.add(data.workProcess[i][j]);
            }
            workProcess.add(workProcessTemp);
        }
        //初始化调度结果
        workResult=new ArrayList<ArrayList<shopStructure>>();
        for(int i=0;i<data.deviceId.length;i++){
            ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
            workResult.add(workResultTemp);
        }
        //初始化工序的开始时间和结束时间
        startTime=new double[data.workProcess.length][6];
        endTime=new double[data.workProcess.length][6];
        for(int i=0;i<startTime.length;i++){
            for(int j=0;j<6;j++){
                startTime[i][j]=-1;
                endTime[i][j]=-1;
            }
        }
    }
    /**
     * 启发策略-最早开始时间
     */
    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);
    }
    /**
     * 启发策略-最早结束时间
     * 随机分配工序
     */
    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);
    }
    /**
     * 贪婪启发策略-最早开始时间
     * 匈牙利算法分配第一工序和设备
     */
    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);
    }
    /**
     * 贪婪启发策略-最早结束时间
     * 匈牙利算法分配第一工序和设备
     */
    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);
    }
    private int[] shopAssign(double[][][] ProcessTime){
        int[] assignResult=new int[data.workId.length];
        double[][] fistProcess=new double[data.workId.length][data.deviceId.length];
        for(int i=0;i<data.workId.length;i++){
            for(int j=0;j<data.deviceId.length;j++){
                fistProcess[i][j]=ProcessTime[i][0][j];
            }
        }
        //匈牙利算法分配第一工序
        HungarianAlgorithm lp2=new HungarianAlgorithm(fistProcess);
        lp2.hungarianMethod();
        int N = lp2.m.length;
        double finalAnswer = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (lp2.m[i][j] == -1) {
                    assignResult[i]=j+1;
                }
            }
        }
//        System.out.println("=================");
//        for(int i=0;i<assignResult.length;i++){
//            System.out.print(assignResult[i]+"\t");
//        }
        return assignResult;
    }
    //数组元素交换
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    //数组随机打乱
    private static void shuffle(int[] arr) {
        Random rand = new Random();
        for (int i = arr.length; i > 0; i--) {
            int randInd = rand.nextInt(i);
            swap(arr, randInd, i - 1);
        }
    }
    //打印结果
    private void printResult(ArrayList<ArrayList<shopStructure>>workResult,double[][] endTime){
        for(int i=0;i<workResult.size();i++){
            System.out.println("=======设备"+(i+1)+"=======");
            for(int j=0;j<workResult.get(i).size();j++){
                System.out.println("工件"+workResult.get(i).get(j).workId+"\t"+"工序"+workResult.get(i).get(j).workProcess);
                System.out.println(workResult.get(i).get(j).startTime+"\t"+workResult.get(i).get(j).endTime+"\t"+data.ProcessTime[workResult.get(i).get(j).workId-1][workResult.get(i).get(j).workProcess-1][workResult.get(i).get(j).deviceId-1]);
            }
        }
        double[] endTimeList=new double[data.workId.length];
        for(int i=0;i<endTime.length;i++){
            endTimeList[i]=getMax(endTime[i]);
            System.out.println("工件"+(i+1)+"完工时间:"+endTimeList[i]);
        }
        System.out.println("最大完工时间:"+getMax(endTimeList));
    }
    //获取数组的最大值
    private double getMax(double[] arr) {
        double max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (max < arr[i]) {
                max = arr[i];
            }
        }
        return max;
    }
    //遗传算法求解
    GAAlgorithm ga=new GAAlgorithm();
    class GAAlgorithm{
        Data data;
        //工件
        shopStructure shop;
        //种群个数
        int popNum;
        //工序个数
        int processNum;
        //迭代次数
        int ITER;
        //工序列表
        ArrayList<Integer>processList;
        //工序编码
        int[][] processCode;
        //设备编码
        int[][] deviceCode;
        //工序可用设备编码
        ArrayList<ArrayList<ArrayList<Integer>>> processDevice;
        //最优工序个体
        int[] processBest;
        //最优设备个体
        int[] deviceBest;
        //最优目标值
        double targetBest;
        //调度结果
        ArrayList<ArrayList<shopStructure>>workResult;
        private GAAlgorithm(){
            data=new Data();
            //初始化工件类
            shop=new shopStructure();
            //种群个数
            popNum=200;
            processNum=0;
            ITER=500;
            for(int i=0;i<data.workProcess.length;i++){
                processNum+=data.workProcess[i].length;
            }
            //工序编码
            processCode=new int[popNum][processNum];
            processList=new ArrayList<>();
            for(int i=0;i<data.workProcess.length;i++){
                for(int j=0;j<data.workProcess[i].length;j++){
                    processList.add(i+1);
                }
            }
            //设备编码
            deviceCode=new int[popNum][processNum];
            //工序可用设备编码
            processDevice=new ArrayList<>();
            for(int i=0;i<data.ProcessTime.length;i++){
                ArrayList<ArrayList<Integer>>processDevice1=new ArrayList<>();
                for(int j=0;j<data.ProcessTime[i].length;j++){
                    double[] deviceTimeList=new double[data.deviceId.length];
                    int[] deviceTimeIndex=new int[data.deviceId.length];
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        deviceTimeList[k]=data.ProcessTime[i][j][k];
                        deviceTimeIndex[k]=k;
                    }
                    //由小到大排序
                    for(int ii=1;ii<deviceTimeList.length;ii++){
                        for(int jj=0;jj<deviceTimeList.length-ii;jj++){
                            if(deviceTimeList[jj]>deviceTimeList[jj+1]){
                                double temp=deviceTimeList[jj+1];
                                int tempIndex=deviceTimeIndex[jj+1];
                                deviceTimeList[jj+1]=deviceTimeList[jj];
                                deviceTimeIndex[jj+1]=deviceTimeIndex[jj];
                                deviceTimeList[jj]=temp;
                                deviceTimeIndex[jj]=tempIndex;
                            }
                        }
                    }
                    ArrayList<Integer>processDevice2=new ArrayList<>();
                    for(int k=0;k<data.ProcessTime[i][j].length;k++){
                        if(deviceTimeList[k]!=Double.MAX_VALUE){
                            processDevice2.add(deviceTimeIndex[k]+1);
                        }
                    }
                    processDevice1.add(processDevice2);
                }
                processDevice.add(processDevice1);
            }
            //最优工序个体
            processBest=new int[processNum];
            //最优设备个体
            deviceBest=new int[processNum];
            //最优目标值
            targetBest=Double.MAX_VALUE;
            //初始化调度结果
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
        }
        //种群初始化-个体编码
        private void initPop(){
            for(int i=0;i<popNum;i++){
                int[] processRamdomIndex=processRandom();
                for(int j=0;j<processNum;j++){
                    processCode[i][j]=processList.get(processRamdomIndex[j]-1);
                }
                int[] deviceRandomIndex=deviceRandom(processCode[i]);
                for(int j=0;j<processNum;j++){
                    deviceCode[i][j]=deviceRandomIndex[j];
                }
            }
        }
        //初始最优个体与适应值
        private void initFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //个体变异:工序随机交换指定偶数位;设备随机选取
        private int[][] mutationPop(int[] processPop,int[] devicePop,boolean mutateType){
            int[][] newPop=new int[2][processPop.length];
            newPop[0]=processPop.clone();
            newPop[1]=devicePop.clone();
            Random random = new Random();
            int mutateNum;
            int[] processIndex;
            //交叉前变异
            if(mutateType){
                mutateNum=2;
                processIndex=new int[mutateNum];
            }
            //交叉后变异
            else{
                //变异位数
                mutateNum=processPop.length/5;
                if(mutateNum%2!=0)mutateNum-=1;
                processIndex=new int[mutateNum];
            }
            //工序变异位置
            for(int i=0;i<processIndex.length;i++){
                processIndex[i]=random.nextInt(newPop[0].length);
            }
//            System.out.println();
//            System.out.println("工序变异位置");
//            for(int i=0;i<processIndex.length;i++){
//                System.out.print((processIndex[i]+1)+"\t");
//            }
//            System.out.println();
            //工序变异
            for(int i=0;i<processIndex.length;i+=2){
                int temp=newPop[0][processIndex[i]];
                newPop[0][processIndex[i]]=newPop[0][processIndex[i+1]];
                newPop[0][processIndex[i+1]]=temp;
            }
//            System.out.println("工序变异结果");
//            for(int j=0;j<newPop[0].length;j++){
//                System.out.print(newPop[0][j]+"\t");
//            }
//            System.out.println();
            //设备变异位置
            for(int i=0;i<processIndex.length;i++){
                //确定工序
                int count=0;
                for(int j=0;j<newPop[0].length;j++){
                    if(newPop[0][j]==newPop[0][processIndex[i]]){
                        newPop[1][j]=random.nextInt(processDevice.get(newPop[0][processIndex[i]]-1).get(count).size());
                        count+=1;
                    }
                }
            }
//            System.out.println("设备变异结果");
//            for(int i=0;i<newPop[1].length;i++){
//                System.out.print(newPop[1][i]+"\t");
//            }
            return newPop;
        }
        //轮盘赌策略-计算选择概率
        private double[] rouletteStrategy(int[][] processCodeChild,int[][] deviceCodeChild){
            double[] selectedProb=new double[popNum];
            double fitnessSum=0;
            //计算个体适应值
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessPop(processCodeChild[i],deviceCodeChild[i],false);
                fitnessSum+=selectedProb[i];
            }
            //反差
            double selectedSum=0;
            for(int i=0;i<popNum;i++){
                selectedProb[i]=fitnessSum-selectedProb[i];
                selectedSum+=selectedProb[i];
            }
            //计算累积概率
            double[] selectedProbTemp=selectedProb.clone();
            for(int i=0;i<popNum;i++){
                if(i==0)selectedProb[i]=selectedProbTemp[i]/selectedSum;
                else selectedProb[i]=(selectedProbTemp[i]/selectedSum)+selectedProb[i-1];
            }
            return selectedProb;
        }
        //个体选择
        private int[] selectPop(double[] selectedProb){
            int[] selectedIndex=new int[2];
            Random random = new Random();
            for(int i=0;i<selectedIndex.length;i++){
                double temp=random.nextDouble(0,1);
                //找到位置索引
                for(int j=0;j<selectedProb.length;j++){
                    if(temp<=selectedProb[j]){
                        selectedIndex[i]=j;
                        break;
                    }
                }
            }
            return selectedIndex;
        }
        //个体交叉
        private int[][] crossPop(int[] selectedIndex){
            int[][] crossResult=new int[4][processNum];
            //随机交叉位置
            int crossNum=processNum/5;
            if(crossNum<=0)crossNum=1;
            int[] crossIndex=new int[crossNum];
            int[] arrTemp=new int[processNum];
            for(int i=0;i<arrTemp.length;i++){
                arrTemp[i]=i;
            }
            shuffle(arrTemp);
            for(int i=0;i<crossIndex.length;i++){
                crossIndex[i]=arrTemp[i];
            }
//            System.out.println("个体1交叉位置:");
//            for(int i=0;i<crossIndex.length;i++){
//                System.out.print(crossIndex[i]+"\t");
//            }
//            System.out.println();
            //找到另一个体上的交叉位置
            int[] anotherCrossIndex=new int[crossNum];
            for(int i=0;i<anotherCrossIndex.length;i++){anotherCrossIndex[i]=-1;}
            for(int i=0;i<crossIndex.length;i++){
                for(int j=0;j<processCode[selectedIndex[1]].length;j++){
                    if(processCode[selectedIndex[1]][j]==processCode[selectedIndex[0]][crossIndex[i]]){
                        //是否已经添加过
                        boolean appendType=false;
                        for(int k=0;k<anotherCrossIndex.length;k++){
                            if(anotherCrossIndex[k]==j){
                                appendType=true;
                                break;
                            }
                        }
                        if(!appendType){
                            anotherCrossIndex[i]=j;
                        }
                    }
                }
            }
//            System.out.println("个体2交叉位置:");
//            for(int i=0;i<anotherCrossIndex.length;i++){
//                System.out.print(anotherCrossIndex[i]+"\t");
//            }
//            System.out.println();
            //交叉
            for(int i=0;i<selectedIndex.length;i++){
                int count=0;
                for(int j=0;j<processNum;j++){
                    if(i==0){
                        if(judgeExist(crossIndex,j)){
                            crossResult[0][j]=processCode[selectedIndex[i]][j];
                            crossResult[2][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(anotherCrossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[0][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[2][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                    else{
                        if(judgeExist(anotherCrossIndex,j)){
                            crossResult[1][j]=processCode[selectedIndex[i]][j];
                            crossResult[3][j]=deviceCode[selectedIndex[i]][j];
                        }
                        else{
                            //另一个体当中选择第一个不在交叉位置的索引
                            while(true){
                                if(!judgeExist(crossIndex,count)){
                                    break;
                                }
                                else{
                                    count++;
                                }
                            }
                            crossResult[1][j]=processCode[selectedIndex[1-i]][count];
                            crossResult[3][j]=deviceCode[selectedIndex[1-i]][count];
                            count++;
                        }
                    }
                }
            }
            //校验设备分配-如果超出索引,则随机分配;
            crossResult[2]=deviceCheck(crossResult[0].clone(),crossResult[2].clone());
            crossResult[3]=deviceCheck(crossResult[1].clone(),crossResult[3].clone());
            return crossResult;
        }
        //子代生成
        private void generateChild(int[][] processCode,int[][] deviceCode,int[][] processCodeChild,int[][] deviceCodeChild){
            //计算原种群适应值
            double[] targetValue=new double[processCode.length+processCodeChild.length];
            int[] targetIndex=new int[processCode.length+processCodeChild.length];
            for(int i=0;i<processCode.length;i++){
                targetValue[i]=fitnessPop(processCode[i].clone(),deviceCode[i].clone(),false);
                targetIndex[i]=i;
            }
            for(int i=0;i<processCodeChild.length;i++){
                targetValue[i+processCode.length]=fitnessPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                targetIndex[i+processCode.length]=i+processCode.length;
            }
            //排序
            for(int i=1;i<targetValue.length;i++){
                for(int j=0;j<targetValue.length-i;j++){
                    if(targetValue[j]>targetValue[j+1]){
                        double temp=targetValue[j+1];
                        int tempIndex=targetIndex[j+1];
                        targetValue[j+1]=targetValue[j];
                        targetIndex[j+1]=targetIndex[j];
                        targetValue[j]=temp;
                        targetIndex[j]=tempIndex;
                    }
                }
            }
            //生成子代
            for(int i=0;i<this.processCode.length;i++){
                if(targetIndex[i]<processCode.length){
                    this.processCode[i]=processCode[targetIndex[i]].clone();
                    this.deviceCode[i]=deviceCode[targetIndex[i]].clone();
                }
                else{
                    this.processCode[i]=processCodeChild[targetIndex[i]-processCode.length].clone();
                    this.deviceCode[i]=deviceCodeChild[targetIndex[i]-processCode.length].clone();
                }
            }
        }
        //更新最优个体与适应值
        private void updateFitness(){
            for(int i=0;i<processCode.length;i++){
                double temp=fitnessPop(processCode[i],deviceCode[i],false);
                if(temp<targetBest){
                    targetBest=temp;
                    processBest=processCode[i].clone();
                    deviceBest=deviceCode[i].clone();
                }
            }
        }
        //判断是否存在
        private boolean judgeExist(int[] arr,int element){
            boolean existType=false;
            for(int i=0;i<arr.length;i++){
                if(arr[i]==element){
                    existType=true;
                    break;
                }
            }
            return existType;
        }
        //计算个体适应值
        private double fitnessPop(int[] processPop,int[] devicePop,boolean rstType){
            double fitness=0;
            fitness=greedyDecode(processPop,devicePop,rstType);
            return fitness;
        }
        //个体解码
        private double greedyDecode(int[] processPop,int[] devicePop,boolean rstType){
            //清空列表
            workResult=new ArrayList<ArrayList<shopStructure>>();
            for(int i=0;i<data.deviceId.length;i++){
                ArrayList<shopStructure> workResultTemp=new ArrayList<shopStructure>();
                workResult.add(workResultTemp);
            }
//            System.out.println("解码个体");
//            for(int i=0;i<processPop.length;i++){
//                System.out.print(processPop[i]+"\t");
//            }
//            System.out.println();
//            for(int i=0;i<devicePop.length;i++){
//                System.out.print(devicePop[i]+"\t");
//            }
//            System.out.println();
            //记录开始和结束时间
            double[][] startTime=new double[data.workId.length][6];
            double[][] endTime=new double[data.workId.length][6];
            for(int i=0;i<startTime.length;i++){
                for(int j=0;j<6;j++){
                    startTime[i][j]=-1;
                    endTime[i][j]=-1;
                }
            }
            //记录工件使用次数-工序
            int[] workList=new int[data.workId.length];
            //分配工序到设备
            for(int i=0;i<processPop.length;i++){
//                System.out.println("工序:");
//                for(int ii=0;ii<workList.length;ii++){
//                    System.out.print(workList[ii]+"\t");
//                }
//                System.out.println();
                int deviceIndex=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).get(devicePop[i]);
                double deviceTime = Double.MAX_VALUE;
                if(workResult.get(deviceIndex-1).size()==0){
                    if(workList[processPop[i]-1]==0){
                        deviceTime=0;
                    }
                    else{
                        deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                    }
                }
                else{
                    //设备已存在分配的工序
                    //第一个工序分配
                    if(workList[processPop[i]-1]==0){
                        deviceTime=workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                    }
                    else{
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime >= endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime+1;
                        }
                        if (workResult.get(deviceIndex-1).get(workResult.get(deviceIndex-1).size() - 1).endTime < endTime[processPop[i]-1][workList[processPop[i]-1]-1]) {
                            deviceTime = endTime[processPop[i]-1][workList[processPop[i]-1]-1]+1;
                        }
                    }
                }
                //更新工件信息
                shop.workId = processPop[i];
                shop.workProcess = workList[processPop[i]-1]+1;
                shop.deviceId = deviceIndex;
                shop.startTime = deviceTime;
                startTime[processPop[i]-1][workList[processPop[i]-1]] = shop.startTime;
                shop.endTime = shop.startTime + data.ProcessTime[processPop[i]-1][workList[processPop[i]-1]][deviceIndex-1];
                endTime[processPop[i]-1][workList[processPop[i]-1]] = shop.endTime;
                workResult.get(deviceIndex-1).add(shop.clone());
                workList[processPop[i]-1]+=1;

            }
            //计算最大完工时间
            double maxTime=maxEndTime(endTime);
            if(rstType)printResult(workResult,endTime);
            return maxTime;
        }
        //计算最大完工时间
        private double maxEndTime(double[][] endTime){
            double maxTime=0;
            double[] endTimeList=new double[data.workId.length];
            for(int i=0;i<endTime.length;i++){
                endTimeList[i]=getMax(endTime[i]);
            }
            maxTime=getMax(endTimeList);
            return maxTime;
        }
        //产生个体长度的随机数-工序
        private int[] processRandom(){
            int[] processSort=new int[processNum];
            double[] processRandomValue=new double[processNum];
            Random random = new Random();
            for(int i=0;i<processNum;i++){
                processRandomValue[i]=random.nextDouble(10);
                processSort[i]=i+1;
            }
            //冒泡排序
            for(int i=1;i<processRandomValue.length;i++){
                for(int j=0;j<processRandomValue.length-i;j++){
                    if(processRandomValue[j]>processRandomValue[j+1]){
                        double temp=processRandomValue[j+1];
                        int tempIndex=processSort[j+1];
                        processRandomValue[j+1]=processRandomValue[j];
                        processSort[j+1]=processSort[j];
                        processRandomValue[j]=temp;
                        processSort[j]=tempIndex;
                    }
                }
            }
            return processSort;
        }
        //产生个体长度的随机数-设备索引
        private int[] deviceRandom(int[] processIndex){
            int[] deviceRandomValue=new int[processNum];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            int count=0;
            for(int i=0;i<processNum;i++){
                deviceRandomValue[count++]=random.nextInt(processDevice.get(processIndex[i]-1).get(workList[processIndex[i]-1]).size());
                workList[processIndex[i]-1]+=1;
            }
            return deviceRandomValue;
        }
        //设备校验
        private int[] deviceCheck(int[] processPop,int[] devicePop){
            int[] checkResult=new int[devicePop.length];
            int[] workList=new int[data.workId.length];
            Random random = new Random();
            for(int i=0;i<devicePop.length;i++){
                int deviceIndexLength=processDevice.get(processPop[i]-1).get(workList[processPop[i]-1]).size();
                if(devicePop[i]<deviceIndexLength){
                    checkResult[i]=devicePop[i];
                }
                else{
                    checkResult[i]=random.nextInt(deviceIndexLength);
                }
                workList[processPop[i]-1]+=1;
            }
            return checkResult;
        }
        private void GAMethod(){
            //初始化
            initPop();
//            System.out.println("初始种群:");
//            for(int i=0;i<popNum;i++){
//                System.out.println("个体"+(i+1)+"工序编码");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(processCode[i][j]+"\t");
//                }
//                System.out.println();
//                System.out.println("个体"+(i+1)+"设备编码索引");
//                for(int j=0;j<processNum;j++){
//                    System.out.print(deviceCode[i][j]+"\t");
//                }
//                System.out.println();
//            }
            //初始最优个体与适应值
            initFitness();
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            //迭代记录
            int iter=0;
            while(iter<ITER){
//                System.out.println();
//                System.out.println("=========第"+(iter+1)+"次迭代=========");
//                System.out.println("最优值:"+targetBest);
                //扰动
                int[][] processCodeTemp=processCode.clone();
                int[][] deviceCodeTemp=deviceCode.clone();
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCode[i].clone(),deviceCode[i].clone(),true);
                    processCodeTemp[i]=mutatePopTemp[0].clone();
                    deviceCodeTemp[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("扰动种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeTemp[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //交叉
                int[][] processCodeChild=processCode.clone();
                int[][] deviceCodeChild=deviceCode.clone();
                double[] selectedProb=rouletteStrategy(processCodeTemp,deviceCodeTemp);
                int crossCount=0;
                for(int i=0;i<processCode.length/2;i++){
                    int[] selectedIndex= selectPop(selectedProb);
                    int[][] crossResult=crossPop(selectedIndex);
                    processCodeChild[crossCount]=crossResult[0].clone();
                    deviceCodeChild[crossCount]=crossResult[2].clone();
                    crossCount++;
                    processCodeChild[crossCount]=crossResult[1].clone();
                    deviceCodeChild[crossCount]=crossResult[3].clone();
                    crossCount++;
                }
//                System.out.println();
//                System.out.println("交叉种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //变异
                for(int i=0;i<popNum;i++){
                    int[][] mutatePopTemp=mutationPop(processCodeChild[i].clone(),deviceCodeChild[i].clone(),false);
                    processCodeChild[i]=mutatePopTemp[0].clone();
                    deviceCodeChild[i]=mutatePopTemp[1].clone();
                }
//                System.out.println();
//                System.out.println("变异种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCodeChild[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //选择
                generateChild(processCode.clone(),deviceCode.clone(),processCodeChild.clone(),deviceCodeChild.clone());
//                System.out.println("子代种群:");
//                for(int i=0;i<popNum;i++){
//                    System.out.println("个体"+(i+1)+"工序编码");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(processCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                    System.out.println("个体"+(i+1)+"设备编码索引");
//                    for(int j=0;j<processNum;j++){
//                        System.out.print(deviceCode[i][j]+"\t");
//                    }
//                    System.out.println();
//                }
                //更新
                updateFitness();
                iter++;
            }
            System.out.println("最优个体:"+targetBest);
            for(int i=0;i<processNum;i++){
                System.out.print(processBest[i]+"\t");
            }
            System.out.println();
            for(int i=0;i<processNum;i++){
                System.out.print(deviceBest[i]+"\t");
            }
            System.out.println();
            greedyDecode(processBest,deviceBest,true);
        }
    }


    public static void main(String[] args){
        FlexibleShopSchedulingProblemDemo lp=new FlexibleShopSchedulingProblemDemo();
        lp.ga.GAMethod();
        //lp.EST();
        //lp.GEST();
        //lp.EET();
        //lp.GEET();
    }
}

import java.util.Arrays;

public class HungarianAlgorithm{
    double[][] a;
    double m[][];
    public HungarianAlgorithm(double[][] a){
        this.a=new double[a.length][a[0].length];
        for(int i=0;i<a.length;i++){
            for(int j=0;j<a[i].length;j++){
                this.a[i][j]=a[i][j];
            }
        }
    }
    public static void main(String[] args) {
        double[][] a = { { 12, 7, 9, 7, 9 }, { 8, 9, 6, 6, 6 }, { 7, 17, 12, 14, 9 }, { 15, 14, 6, 6, 10 },
                { 4, 10, 7, 10, 9 } };
        HungarianAlgorithm lp=new HungarianAlgorithm(a);
        lp.hungarianMethod();


    }
    public void hungarianMethod(){
        int L = a.length;
        int S = a[1].length;
        if (L <= S) {
            double[][] b = new double[S - L][S];
            for (int i = 0; i < S - L; i++) {
                for (int j = 0; j < S; j++) {
                    b[i][j] = 0;
                }
            }

            m = Arrays.copyOf(a, a.length + b.length);
            System.arraycopy(b, 0, m, a.length, b.length);

            int N = m.length;
            double[][] c = new double[N][N];
            c = copy(m, c);

            guiyue(m);
            tryAppoint(m);


            double finalAnswer = 0;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (m[i][j] == -1) {
                        finalAnswer += c[i][j];
                    }
                }
            }
        }
    }
    public static void guiyue(double[][] m) {
        int N = m.length;

        for (int i = 0; i < N; i++) {
            double min = Double.MAX_VALUE;
            for (int j = 0; j < N; j++) {
                if (m[i][j] < min) {
                    min = m[i][j];
                }
            }

            for (int j = 0; j < N; j++) {
                m[i][j] -= min;

            }
        }

        for (int j = 0; j < N; j++) {
            double min = Double.MAX_VALUE;
            for (int i = 0; i < N; i++) {
                if (m[i][j] < min) {
                    min = m[i][j];
                }
            }
            for (int i = 0; i < N; i++) {
                m[i][j] -= min;
            }
        }

    }

    public static void tryAppoint(double[][] m) {
        int N = m.length;

        int zeroNumber = 0;
        int zeroNumber1 = -1;

        while (zeroNumber1 != zeroNumber) {
            zeroNumber1 = zeroNumber;
            rowsAppoint(m);
            colsAppoint(m);
            zeroNumber = 0;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (m[i][j] == 0) {
                        zeroNumber += 1;
                    }
                }
            }

        }
        do {
            rowsAppoint2(m);
        } while (rowsAppoint2(m));


        if (judge(m)) {
            printResult(m);
        } else {
            drawZeroLine(m);
        }
    }

    public static void drawZeroLine(double[][] m) {
        int N = m.length;

        boolean[] rowIsChecked = new boolean[N];
        boolean[] colIsChecked = new boolean[N];

        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (m[i][j] == -1) {
                    rowIsChecked[i] = false;
                    break;
                } else {
                    rowIsChecked[i] = true;
                }
            }
        }

        for (int j = 0; j < N; j++) {
            colIsChecked[j] = false;
        }

        int trueCount = 0;
        int trueCount1 = -1;

        while (trueCount1 != trueCount) {
            trueCount1 = trueCount;

            for (int i = 0; i < N; i++) {
                if (rowIsChecked[i]) {
                    for (int j = 0; j < N; j++) {
                        if (m[i][j] == -2) {
                            colIsChecked[j] = true;
                        }
                    }
                }
            }

            for (int j = 0; j < N; j++) {
                if (colIsChecked[j]) {
                    for (int i = 0; i < N; i++) {
                        if (m[i][j] == -1) {
                            rowIsChecked[i] = true;
                        }
                    }
                }
            }

            trueCount = 0;
            for (int i = 0; i < N; i++) {
                if (rowIsChecked[i] == true) {
                    trueCount += 1;
                }
            }
            for (int j = 0; j < N; j++) {
                if (colIsChecked[j] == true) {
                    trueCount += 1;
                }
            }
        }

        int lineCount = 0;
        for (int i = 0; i < N; i++) {
            if (rowIsChecked[i] == false) {
                lineCount += 1;
            }
        }
        for (int j = 0; j < N; j++) {
            if (colIsChecked[j] == true) {
                lineCount += 1;
            }
        }
        if (lineCount < N) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (m[i][j] < 0) {
                        m[i][j] = 0;
                    }
                }
            }
            double minValue = Double.MAX_VALUE;
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (rowIsChecked[i] == true && colIsChecked[j] == false) {
                        if (m[i][j] < minValue) {
                            minValue = m[i][j];
                        }
                    }
                }
            }

            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (rowIsChecked[i] == true) {
                        m[i][j] -= minValue;
                    }
                    if (colIsChecked[j] == true) {
                        m[i][j] += minValue;
                    }
                }
            }
            tryAppoint(m);
        } else if (lineCount == N) {
            for (int i = 0; i < N; i++) {
                for (int j = 0; j < N; j++) {
                    if (m[i][j] < 0) {
                        m[i][j] = 0;
                    }
                }
            }

            tryAppoint(m);
        }
    }

    public static void rowsAppoint(double[][] m) {
        int N = m.length;
        for (int i = 0; i < N; i++) {
            int rowCount = 0;
            int colIndex = Integer.MIN_VALUE;
            for (int j = 0; j < N; j++) {
                if (m[i][j] == 0) {
                    rowCount += 1;
                    colIndex = j;
                }
            }

            if (rowCount == 1) {
                m[i][colIndex] = -1;
                for (int i1 = 0; i1 < N; i1++) {

                    if (i1 == i) {
                        continue;
                    } else if (m[i1][colIndex] == 0) {
                        m[i1][colIndex] = -2;
                    }
                }

            }

        }
    }

    public static boolean rowsAppoint2(double[][] m) {
        boolean zeroExist = false;
        int N = m.length;
        int rowZeroNumber = Integer.MAX_VALUE;

        for (int i = 0; i < N; i++) {
            int rowZeroCount = 0;
            for (int j = 0; j < N; j++) {
                if (m[i][j] == 0) {
                    rowZeroCount += 1;
                    zeroExist = true;
                }
            }
            if (rowZeroCount < rowZeroNumber && rowZeroCount != 0) {
                rowZeroNumber = rowZeroCount;

            }
        }

        for (int i = 0; i < N; i++) {
            int rowCount = 0;
            int colIndex = Integer.MAX_VALUE;
            for (int j = 0; j < N; j++) {
                if (m[i][j] == 0) {
                    rowCount += 1;
                    colIndex = j;
                    zeroExist = true;
                }
            }
            if (rowCount == rowZeroNumber) {
                if (Math.random() > 0.95) {
                    for (int i1 = 0; i1 < N; i1++) {
                        if (m[i1][colIndex] == 0) {
                            m[i1][colIndex] = -2;
                        }
                    }
                    for (int j1 = 0; j1 < N; j1++) {
                        if (m[i][j1] == 0) {
                            m[i][j1] = -2;
                        }
                    }
                    m[i][colIndex] = -1;

                    break;
                }
            }
        }
        return zeroExist;
    }

    public static void colsAppoint(double[][] m) {
        int N = m.length;
        for (int j = 0; j < N; j++) {
            int colCount = 0;
            int rowIndex = Integer.MIN_VALUE;
            for (int i = 0; i < N; i++) {
                if (m[i][j] == 0) {
                    colCount += 1;
                    rowIndex = i;
                }
            }

            if (colCount == 1) {
                m[rowIndex][j] = -1;
                for (int j1 = 0; j1 < N; j1++) {

                    if (j1 == j) {
                        continue;
                    } else if (m[rowIndex][j1] == 0) {
                        m[rowIndex][j1] = -2;
                    }
                }

            }
        }

    }

    public static boolean judge(double[][] m) {
        int count = 0;
        int N = m.length;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (m[i][j] == -1) {
                    count += 1;
                }
            }
        }
        return count == N;
    }

    public static double[][] copy(double[][] m, double[][] a) {
        int N = m.length;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                a[i][j] = m[i][j];
            }
        }
        return (a);
    }

    public static void printM(double[][] m) {
        int N = m.length;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                System.out.print(m[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void printResult(double[][] m) {
        int N = m.length;
        double finalAnswer = 0;
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                if (m[i][j] == -1) {
                    System.out.print((i + 1) + "--" + (j + 1) + "  ");
                }
            }
        }
    }
}

========================================
今天到此为止,后续记录其他应用问题的学习过程。
以上学习笔记,如有侵犯,请立即联系并删除!

  • 6
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南音小榭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值