遗传算法(GA)求解带约束的流水车间(flow shop)调度问题——python

本文介绍了一个使用遗传算法解决工作流程调度问题的案例。问题涉及到28个任务在8台机器上的顺序处理,其中特定任务有选择性处理约束。算法通过编码、初始化种群、交叉、变异、适应度计算、种群选择和进化等步骤进行优化,最终减少了最后一个任务的完成时间。优化后的排程使得资源利用率更高,空闲时间减少。提供的Python代码实现了整个遗传算法流程。
摘要由CSDN通过智能技术生成

算法分析与设计课程的期末大作业,问题如下:

Problem 2: job scheduling problem.

The production line has 8 machines, and 28 jobs are ready to be processed. The process time of each job to the machine is given as follows, the number in the table is the process time of each job on each machine:

 

machine1

machine2

machine3

machine4

machine5

machine6

machine7

machine8

Job1

11

16

15

4

15

22

17

22

Job2

9

9

15

3

21

4

16

2

Job3

15

15

19

15

22

15

17

8

Job4

22

33

16

19

21

21

27

9

Job5

14

28

14

12

12

17

8

21

Job6

7

18

19

11

10

23

13

19

Job7

18

19

20

5

29

16

9

10

Job8

8

32

32

16

1

12

8

5

Job9

23

22

14

18

22

11

11

5

Job10

9

17

27

45

12

11

8

6

Job11

21

15

11

10

10

13

17

29

Job12

8

8

12

12

12

16

16

16

Job13

7

7

27

7

7

7

19

9

Job14

11

11

11

12

25

33

11

11

Job15

19

5

5

5

7

8

21

21

Job16

21

12

11

3

3

3

5

5

Job17

7

7

4

4

12

12

16

16

Job18

8

1

1

8

8

1

1

1

Job19

11

22

22

33

33

11

11

22

Job20

11

35

33

18

6

6

6

2

Job21

2

2

33

1

1

51

22

2

Job22

15

15

15

5

5

5

5

5

Job23

19

2

19

2

2

2

19

2

Job24

18

18

38

2

2

22

38

3

Job25

10

9

9

29

1

1

1

22

Job26

50

1

1

5

2

1

44

1

Job27

35

7

7

12

12

35

1

5

Job28

22

1

1

29

11

29

1

1

We suppose:

Each machine can process one job at one time.

The process of each job at each machine cannot be spitted.

Each job has to be processed along machines based on sequence 1,2,3,, ,8  (for example, any job cannot be processed on machine 2 before machine 1, it cannot be processed on machine 3 before machines 1 or 2).

For Jobs 3,5,7,9,11,14 you can choose only 3 job to process, and the others can be ignored.

You need give a process sequence of jobs, such that can minimize the final complete time (at last machine) of last job.

This process sequence cannot be changed along all the machines. For example, once the sequence is 1-2-3-…-27-28, then, for each machine, the job 1 is processed first, then job 2,…, and job28 is the last process job on each machine.

程序设计思路:

一、编码

28个job需要按照确定的时间依次在8台machine上完成8道工序,job间的加工顺序一旦确定在每台machine上都保持一致,同时需要满足job 3,5,7,9,11,14中只有3个被加工,这是一个带有约束的flow shop问题。在进行染色体编码时可以采用如下方式:

每条染色体由31个{1,2,…,28}间的整数排列而成,其中前28个数为head,可以理解为不考虑约束情况下包含28个job的flow shop的加工顺序,后3个数为tail,tail为{3,5,7,9,11,14}中的任意3个不重复的数。则把每条染色体tail部分包含的数从head部分去除将会对应一个原带有约束的flow shop问题的解,如:染色体[8,3,4,…,9,7,6,…,11,28,10,…,3,7,11]表示[8,4,…,9,6,…,28,10…]的加工顺序。

二、初始化种群(initial)

根据种群规模pop_size,将{1,2,…,28}随机打乱pop_size次以获得head,从{3,5,7,9,11,14}中任取3个数,重复操作pop_size次以获得tail,将head和tail拼接,获得初始种群每个个体的染色体。

三、交叉(cross over)

父代种群中的两条染色体(parent1,parent2),以p_cr的概率进行交叉产生子代(child1, child2)。为了保证子代的可行性,可以设计如下交叉方式:

对于head部分,采取单点交叉,即令两条父代染色体的head部分从某一任意索引后的基因片段互换,然后分别遍历两条父代染色体head部分的基因,将没有被包含进子代染色体中的基因依次加入,直到子代染色体的head长度也为28,形成两个子代染色体的head,如下图(以1到7的序列示意):

对于tail部分,任取1~3间的数i,若parent1的tail中没有parent2的tail部分第i位对应的基因(gene2),则将parent1 tail部分的第i个基因(gene1)替换位gene2,反之保持不变;同理,若parent2的tail部分没有gene1,则进行替换,反之不替换,如:

四、变异(mutate)

对每个交叉产生的子代染色体child,以p_mu的概率发生变异,变异规则为:对于head部分,任意生成两个1到28间的索引值,使索引值区间内的基因片段逆序排列,对于tail部分,使其内的任意一位基因替换为{3,5,7,9,11,14}中不被包含在原来的tail内的数,如(head以1到7的序列为例):

五、适应度计算(fitness)利用动态规划的思想可以求解每个解对应的加工顺序的完工时间,令seq表示某染色体代表的加工顺序,dij,i=1,2…8, j=1,2,…,28表示第 个job在第 台machine上的加工时间,Tij,  i=1,2,…,8, j=1,2,…,25表示第i个machine完成seq中第j个job的时间,则有:

T8,25为最大完工时间,将最大完工时间的倒数作为染色体的适应度。

六、种群选择(selection)

采用精英保留法与轮盘赌结合的方式筛选种群,从每一代交叉变异后的种群中选出一定数量的适应度最高的个体直接进入下一代,接着采用轮盘赌的方式从未被选中的个体中选择,直到下一代种群规模达到预设值,适应度越高的个体被选中的几率越大。

七、进化(evolution)

进化过程如下:

八、求解

设定种群规模为80,交叉概率为0.8,变异概率0.01,保留精英解数目为20,进化500代,记录第一代中完工时间的最小值为546,最后一代中完工时间的最小值为473,缩短了13.4%,最优加工顺序为{21, 15, 6, 12, 17, 1, 19, 24, 5, 18, 2, 28, 23, 14, 13, 25, 3, 4, 26, 8, 27, 10, 16, 20, 22}

两种加工顺序的甘特图如下:

可以发现,优化后的甘特图排程明显更加紧密,空置时间较少。

python代码如下,水平有限大家多多交流哇:

from datetime import time
import pandas as pd
import numpy as np
import random
import heapq
import matplotlib.pyplot as plt

class GA_TSP(object):

    pop_size = 50    # 种群规模
    p_cr = 0.8       # 交换概率
    p_mu = 0.01      # 变异概率
    constraints=[3,5,7,9,11,14]    # 约束条件
    job_num=28                     # job数量
    machine_num=8                  # 机器数量
    elite_number=20                # 每一代中精英解的数量
    minimize_complete_time=[]      # 最小完工时间
    best_solve=[]                  # 最佳作业排序
    work_time = np.array(pd.read_csv('工作时间.csv', header=None, engine='python'))

    def __init__(self,popsize,p_cr,p_mu,iteration,elite_number):
        self.pop_size=popsize
        self.p_cr=p_cr
        self.p_mu=p_mu
        self.iteration=iteration
        self.elite_number=elite_number

    def initial(self):
        pop=[]

        for i in range(self.pop_size):
            gene1=np.arange(1,self.job_num+1)
            np.random.shuffle(gene1)
            gene1=gene1.tolist()
            gene2=random.sample(self.constraints,3)
            gene1.extend(gene2)
            pop.append(gene1)

        return np.array(pop)        #初始化种群

    def cross_over(self,parent1,parent2):
        child1=[]
        child2=[]

        if np.random.rand()>self.p_cr:
            return np.zeros(self.job_num+3),np.zeros(self.job_num+3)
        else:
            index1=np.random.randint(0,self.job_num)
            index2=np.random.randint(self.job_num,self.job_num+3)
            for i in range(index1,self.job_num):
                child1.append(parent2[i])
            for i in range(self.job_num):
                if parent1[i] not in child1:
                    child1.append(parent1[i])
            for i in range(self.job_num,self.job_num+3):
                child1.append(parent1[i])
            if parent2[index2] not in child1[-3:]:
                child1[index2]=parent2[index2]

            for i in range(index1,self.job_num):
                child2.append(parent1[i])
            for i in range(self.job_num):
                if parent2[i] not in child2:
                    child2.append(parent2[i])
            for i in range(self.job_num,self.job_num+3):
                child2.append(parent2[i])
            if parent1[index2] not in child2[-3:]:
                child2[index2]=parent1[index2]

            return np.array(child1),np.array(child2)    #交叉

    def mutate(self,gene):

        if np.random.rand()>self.p_mu:
            return gene
        else:
            index1=np.random.randint(0,self.job_num)
            index2=np.random.randint(index1,self.job_num)
            index3=np.random.randint(self.job_num,self.job_num+3)
            gene_slice=gene[index1:index2].copy()
            gene_slice_reverse=gene_slice[::-1]

            for i in range(len(gene_slice)):
                gene[index1+i]=gene_slice_reverse[i]
            constraints_rem=self.constraints.copy()

            for i in gene[-3:]:
                constraints_rem.remove(i)
            gene_insert=random.choice(constraints_rem)
            gene[index3]=gene_insert

            return gene                                #变异

    def fitness(self,pop):
        c_time=[]
        fitness_all=[]

        for i in range(pop.shape[0]):
            pop_cal=pop[i].copy().tolist()
            pop_cal_head=pop_cal[:self.job_num].copy()
            pop_cal_tail=pop_cal[-3:].copy()

            for j in pop_cal_tail:
                pop_cal_head.remove(j)
            ctime_dp=np.zeros((self.machine_num,self.job_num-3))

            for ii in range(self.job_num-3):
                for iii in range(ii+1):
                    ctime_dp[0][ii]=ctime_dp[0][ii]+self.work_time[pop_cal_head[iii]-1][0]
            for ii in range(1,self.machine_num):
                for iii in range(ii+1):
                    ctime_dp[ii][0]=ctime_dp[ii][0]+self.work_time[pop_cal_head[0]-1][iii]
            for ii in range(1,self.job_num-3):
                for iii in range(1,self.machine_num):
                    ctime_dp[iii][ii]=max(ctime_dp[iii-1][ii],ctime_dp[iii][ii-1])+\
                                      self.work_time[pop_cal_head[ii]-1][iii]

            c_time.append(ctime_dp[self.machine_num-1][self.job_num-4])
            fitness_all.append(1/ctime_dp[self.machine_num-1][self.job_num-4])
        index=c_time.index(min(c_time))
        best_solve_iter=pop[index].copy()

        return min(c_time),fitness_all,best_solve_iter      #动态规划求解最大完工时间,计算适应度

    def selection(self,pop,fitness):
        pop_after_select=[]
        pop_before_select=pop.copy().tolist()
        index1=list(map(fitness.index,heapq.nlargest(self.elite_number,fitness)))

        for i in index1:
            pop_after_select.append(pop_before_select[i])
        pop_before_select=[pop_before_select[i] for i in range(len(pop_before_select)) \
                           if i not in index1]
        fitness=[fitness[i] for i in range(len(fitness)) \
                           if i not in index1]
        probability=np.array(fitness)/np.array(fitness).sum()
        index2=np.random.choice(np.arange(len(fitness)),size=self.pop_size-self.elite_number,\
                                replace=False,p=probability)

        for i in index2:
            pop_after_select.append(pop_before_select[i])

        return np.array(pop_after_select)                  #筛选种群:精英保留法+轮盘赌

    def evolution(self):
        population=self.initial()

        for i in range(self.iteration):
            for ii in range(self.pop_size):
                iii=np.random.randint(0,self.pop_size-1)
                if ii != iii:
                    child1,child2=self.cross_over(population[ii],population[iii])
                    if child1[0] != 0:
                        mut_child1=self.mutate(child1)
                        mut_child2=self.mutate(child2)
                        population=np.vstack((population,mut_child1))
                        population=np.vstack((population,mut_child2))

            best_time_iter,fitness_iter,best_solve_iter=self.fitness(population)
            self.minimize_complete_time.append(best_time_iter)
            self.best_solve.append(best_solve_iter)
            population=self.selection(population,fitness_iter)

        return self.minimize_complete_time,self.best_solve   #进化

ga=GA_TSP(80,0.8,0.01,500,20)
solution_time,solution_schedule=ga.evolution()

worst_schedule=list(solution_schedule[0].copy())
worst_schedule_head=worst_schedule[:28]
worst_schedule_tail=worst_schedule[-3:]
for i in worst_schedule_tail:
    worst_schedule_head.remove(i)                           #第一代的排班方案

best_schedule=list(solution_schedule[-1].copy())
best_schedule_head=best_schedule[:28]
best_schedule_tail=best_schedule[-3:]
for i in best_schedule_tail:
    best_schedule_head.remove(i)                            #最后一代的排班方案


def gantt(n,m,seq,wt):

    t = np.zeros(n)
    for i in range(m):
        for j in range(n):
            if j == 0:
                t[j]=t[j]+wt[seq[j]-1][i]
                plt.barh(y=i+1,left=t[j]-wt[seq[j]-1][i],height=1,width=wt[seq[j]-1][i])
            else:
                t[j]=max(t[j],t[j-1])+wt[seq[j]-1][i]
                plt.barh(y=i+1,left=t[j]-wt[seq[j]-1][i],height=1,width=wt[seq[j]-1][i])
            if (wt[seq[j]-1][i] != 0):
                pass

    plt.plot([t[-1], t[-1]], [-0.5, m + 1], color=(0, 0, 0))
    x_tickss = [i for i in range(0, int(t[-1]), int((t[-1] / 4) // 10) * 10)]

    if (t[-1] - x_tickss[-1] < (int((t[-1] / 4) // 10) * 10) / 4):
        x_tickss.pop()
    x_tickss.append(t[-1])
    plt.title('n=%d,m=%d,need_time=%.3f' % (n, m, t[-1]))
    plt.xticks(x_tickss)
    plt.show()

    return (t)                                             #绘制甘特图

gantt(ga.job_num-3,ga.machine_num,worst_schedule_head,ga.work_time)
gantt(ga.job_num-3,ga.machine_num,best_schedule_head,ga.work_time)

 

地震勘探逆时偏移(Reverse Time Migration, RTM)是一种基于波动方程的成像方法,可以用于提高地下结构的图像分辨率。以下是一个简单的C语言实现的RTM算法程序: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define NX 100 // X方向采样点数 #define NZ 100 // Z方向采样点数 #define DT 0.001 // 时间步长 #define DX 10.0 // X方向采样间距 #define DZ 10.0 // Z方向采样间距 #define VP 2000 // 声波速度 float p0[NX][NZ]; // 初始压力场 float p1[NX][NZ]; // 当前压力场 float p2[NX][NZ]; // 下一时间步压力场 float v[NX][NZ]; // 速度模型 // 计算速度模型 void init_v() { int i, j; for (i = 0; i < NX; i++) { for (j = 0; j < NZ; j++) { if (i >= NX / 2 && i <= NX * 3 / 4 && j >= NZ / 4 && j <= NZ * 3 / 4) { v[i][j] = VP * 1.5; } else { v[i][j] = VP; } } } } // 计算初始压力场 void init_p() { int i, j; float x, z; for (i = 0; i < NX; i++) { for (j = 0; j < NZ; j++) { x = (i - NX / 2) * DX; z = (j - NZ / 2) * DZ; p0[i][j] = 0.1 * exp(-x * x / 10000 - z * z / 10000); } } } // 执行一次时间步 void time_step() { int i, j; float dx, dz, c1, c2, c3, c4; for (i = 1; i < NX - 1; i++) { for (j = 1; j < NZ - 1; j++) { dx = DX / v[i][j]; dz = DZ / v[i][j]; c1 = (p1[i + 1][j] - 2 * p1[i][j] + p1[i - 1][j]) / (dx * dx); c2 = (p1[i][j + 1] - 2 * p1[i][j] + p1[i][j - 1]) / (dz * dz); c3 = (p0[i][j] + p1[i][j]) / 2; c4 = (p1[i + 1][j] - p1[i - 1][j]) / (2 * DX) + (p1[i][j + 1] - p1[i][j - 1]) / (2 * DZ); p2[i][j] = 2 * p1[i][j] - p0[i][j] + c1 * DT * DT + c2 * DT * DT - c3 * DT * DT + c4 * DT; } } } // 执行RTM算法 void rtm() { int n, i, j; init_v(); init_p(); for (n = 0; n < 1000; n++) { time_step(); // 边界处理(反射) for (i = 0; i < NX; i++) { p2[i][0] = -p2[i][1]; p2[i][NZ - 1] = -p2[i][NZ - 2]; } for (j = 0; j < NZ; j++) { p2[0][j] = -p2[1][j]; p2[NX - 1][j] = -p2[NX - 2][j]; } // 交换压力场 for (i = 0; i < NX; i++) { for (j = 0; j < NZ; j++) { p0[i][j] = p1[i][j]; p1[i][j] = p2[i][j]; } } } } int main() { rtm(); return 0; } ``` 这个程序实现了一个简单的二维RTM算法,其速度模型是一个简单的区域模型,初始压力场是一个高斯形状的脉冲。程序使用了二阶心差分离散化波动方程,并使用了交错网格(staggered grid)来避免数值耦合。程序还使用了边界处理(反射)来模拟波在边界处反射的现象。由于RTM算法计算量较大,本程序只执行了1000次时间步。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值