问题描述与算法设计
问题背景
(1)参数值:
- 初始化种群数目:NP=自定
- 最大进化代数:G=自定
- 交叉概率:Pc =0.8
- 变异概率:Pm =0.2
(2)编程中的关键问题:
- (a) 编码。每个染色体编码成工件号序列,比如5个工件的一个编码为序列:[5 1 3 4 2]。
- (b) 初始种群。初始种群随机产生,种群规模自定。
- © 解码。将编码按照从左到右的次序进行加工。比如编码[5 1 3 4 2],可以解码为:工件加工次序为:工件5、工件1、工件3、工件4、工件2
- (d)适应度值。因为是流水线调度问题最小值问题,所以适应度值取目标值的倒数。
- (e) 选择。采用轮盘赌选择。
- (f)交叉。交叉采用单点交叉,由于交叉后的个体可能是不可行的,注意需要修复。
- (g)变异。每次变异,从交换、插入、逆序中随机选择一个执行。
- (h)测试算例。使用如下6组(n是工件数,m是机器数,Tm,n是加工时间表,从1到99整数中随机产生):
- 第一组:n=8, m=6, Tm,n
- 第二组:n=10, m=8, Tm,n
- 第三组:n=12, m=10, Tm,n
- 第四组:n=20, m=6, Tm,n
- 第五组:n=25, m=8, Tm,n
- 第六组:n=30, m=10, Tm,n
基本遗传算法的一般步骤
- 第一步:使用随机方法产生一个有NP个染色体的初始种群;
- 第二步:计算种群中每一个染色体的适应度值,即函数值;
- 第三步:若达到最大迭代次数G,则算法终止;否则,继续迭代;
- 第四步:按照轮盘赌选择。
- 第五步:按照概率执行单点交叉操作,注意需要修复;
- 第六步:从交换、插入、逆序中随机选择一个执行变异;
- 第七步:返回第三步。
简单实现
import copy
import numpy as np
import random
class Node:
def __init__(self,data,fitness):
self.data = data
self.fitness = fitness
#种群条件数据
init_data = [[1,31,41,25,30],
[2,19,55,3,34],
[3,23,42,27,6],
[4,13,22,14,13],
[5,33,5,57,19]]
init_data = np.array(init_data)
#基本数据
source_data = {'1':[1,31,41,25,30],
'2':[2,19,55,3,34],
'3':[3,23,42,27,6],
'4':[4,13,22,14,13],
'5':[5,33,5,57,19]
}
#初始化种群
#order_list:初始化种群时的顺序,后期可以调为随机。
order_list1 = [4,2,5,1,3]
class GA_FSP:
def __init__(self,pc=0.6,pm=0.1,N=20,T=50,n=5,machine=4):
'''
N = 20 #群体大小
T = 50 #Ga终止进化代数
Pc = 0.6 #交叉概率
Pm=0.1 #变异概率
n = 5 #工件个数
machine = 4 #机器个数
'''
# self.data = data
self.pc = pc
self.pm = pm
self.N = N
self.T = T
self.n = n
self.machine = machine
def fitness(self,order_list):
'''
输入状态数据,即可输出适应度和加工时间
'''
def init_group(order_list):
'''
根据列表顺序调整整体数据。
'''
three_init_data = copy.deepcopy(order_list)
# print(three_init_data)
for j in range(len(order_list)):
i = order_list[j]
order_i = str(i)
init_i = i-1
three_init_data[j] = source_data[order_i]
init_group = np.array(three_init_data)
return init_group
init_data = init_group(order_list)
# k为加工工件数量
# j为机器数量
k = init_data.shape[0]
j = init_data.shape[1]-1
def find_time(k,j):
if k==1 and j==1:
return init_data[0,1]
elif k>=2 and j==1:
return find_time(k-1,1)+init_data[k-1,1]
elif k == 1 and j>=2:
return find_time(1,j-1)+init_data[0,j]
else:
time = max(find_time(k-1,j),find_time(k,j-1))+init_data[k-1,j]
return time
time = find_time(k,j)
fitness = 1/time
return fitness,time
def init_list(self,N):
N_group = []
for i in range(N):
order_list = [p for p in range(1,self.n+1)]
random.shuffle(order_list)
# node = Node(order_list,fitness(order_list))
N_group.append(order_list)
return N_group
def where_change(self,three_change_list):
for i in range(len(three_change_list)):
if three_change_list[i]<=0:
return i
continue
def cross_change(self,choice_three):
changed_list = []
'''
函数:两点交叉 传入数据为node列表 长度20
'''
def change_main(a1,a2):
'''
两点交叉
传入数据为两个个体
'''
# print("两个父类")
# print(a1.data)
# print(a2.data)
a1_copy = copy.deepcopy(a1)
a2_copy = copy.deepcopy(a2)
random_1=random.randint(0,self.n-1)
random_2=random.randint(0,self.n-1)
while random_1 > random_2 or random_1 == random_2:
random_1 = random.randint(0,self.n-1)
random_2 = random.randint(0,self.n-1)
a1_copy.data[random_1:random_2],a2_copy.data[random_1:random_2]=a2_copy.data[random_1:random_2],a1_copy.data[random_1:random_2]
intermediate1 = a1.data[random_1:random_2]
intermediate2 = a2.data[random_1:random_2]
# print("两个随机数")
# print(random_1)
# print(random_2)
head1 = []
head2 = []
tail1 = []
tail2 = []
# 子代1头
# print("head1")
for i in a1_copy.data[:random_1]:
# print(i)
while i in intermediate2:
i = intermediate1[intermediate2.index(i)]
head1.append(i)
# 子代1尾
# print("tail1")
for i in a1_copy.data[random_2:]:
while i in intermediate2:
i = intermediate1[intermediate2.index(i)]
tail1.append(i)
# 子代2头
# print("head2")
for i in a2_copy.data[:random_1]:
while i in intermediate1:
i = intermediate2[intermediate1.index(i)]
head2.append(i)
# 子代2尾
# print("tail2")
for i in a2_copy.data[random_2:]:
while i in intermediate1:
i = intermediate2[intermediate1.index(i)]
tail2.append(i)
result1 = head1 + intermediate2 + tail1
result2 = head2 + intermediate1 + tail2
# print("两个子类")
# print(result1)
# print(result2)
result1 = Node(result1,self.fitness(result1))
result2 = Node(result2,self.fitness(result2))
return result1,result2
male = choice_three[0:len(choice_three):2]
female = choice_three[1:len(choice_three):2]
if len(male) != len(female):
minmale = min(len(male),len(female))
else:
minmale = len(male)
for j in range(minmale):
cross_rand = random.random()
if cross_rand<self.pc:
a1,a2 = change_main(male[j],female[j])
changed_list.append(a1)
changed_list.append(a2)
else:
changed_list.append(male[j])
changed_list.append(female[j])
# print(changed_list)
return changed_list
def variation_change(self,cross_change_three):
changed_variation=[]
for j in range(len(cross_change_three)):
variation_rand = random.random()
if variation_rand<self.pm:
random_1=random.randint(0,self.n-1)
random_2=random.randint(0,self.n-1)
# print("变异之前的数据",cross_change_three[j].data)
cross_change_three[j].data[random_1],cross_change_three[j].data[random_2]=cross_change_three[j].data[random_2],cross_change_three[j].data[random_1]
changed_variation.append(cross_change_three[j])
# print("变异之后的数据",cross_change_three[j].data)
else:
changed_variation.append(cross_change_three[j])
return changed_variation
def __sort_threelist(self,arr):
'''
冒泡排序
'''
n = len(arr)
for i in range(n):
for j in range(0,n-i-1):
if arr[j].fitness[1]>arr[j+1].fitness[1]:
arr[j],arr[j+1] = arr[j+1] ,arr[j]
return arr
def pro_go(self):
#初始化种群
N_group = self.init_list(self.N)
for j in range(len(N_group)):
N_group[j] = Node(N_group[j],self.fitness(N_group[j]))
#开始迭代
draw_x = []
draw_y = []
for num in range(self.T+1):
#计算适应度
for j in range(len(N_group)):
N_group[j].fitness = self.fitness(N_group[j].data)
# print(N_group[j].fitness[0])
# print(N_group)
if num>self.T:
break
#选择操作
choiceList = [N_group[i].fitness[1] for i in range(len(N_group))]
# print(choiceList)
'''
求出个体i被选中的概率
'''
choice = copy.deepcopy(choiceList)
choice = [choiceList[j]/sum(choiceList) for j in range(len(choiceList))]
# print(choice)
sum_choice = []
for p in range(len(choice)):
if p == 0 :
sum_choice.append(choice[p])
else:
sum_choice.append(choice[p] + sum_choice[p-1])
# print(sum_choice)
# print(rand_num)
'''
选数 原先列表有几行就要选出几个
'''
choice_three = []
for i in range(self.N):
rand_num = random.random()
three_change_list = [rand_num-sum_choice[i] for i in range(len(sum_choice))]
change_index = self.where_change(three_change_list)
choice_three.append(N_group[change_index])
'''
交叉
传入种群节点即可
'''
cross_choice_three = self.cross_change(choice_three)
# print(choice_three)
'''
变异
'''
variation_three = self.variation_change(cross_choice_three)
sort_group= N_group+variation_three
print("第"+str(num)+"次迭代",end=" ")
N_group = self.__sort_threelist(sort_group)[0:self.N]
print(N_group[0].fitness[1],end='--')
print(N_group[-1].fitness[1])
# print()
draw_x.append(num)
draw_y.append(N_group[0].fitness[1])
random.shuffle(N_group)
N_group = self.__sort_threelist(sort_group)[0:self.N]
for i in range(len(N_group)):
print(N_group[i].data)
return N_group[i],draw_x,draw_y
Ga1 = GA_FSP()
best1,draw_x,draw_y = Ga1.pro_go()
final_data = best1.data
import matplotlib.pyplot as plt
plt.plot(draw_x, draw_y)
plt.show()
加入测试算例(时间略长)
import copy
import numpy as np
import random
import matplotlib.pyplot as plt
import time
import eventlet
plt.rcParams['font.sans-serif']=['SimHei'] #显示中文标签
plt.rcParams['axes.unicode_minus']=False #这两行需要手动设置
class Node:
def __init__(self,data,fitness):
self.data = data
self.fitness = fitness
#初始化种群
#order_list:初始化种群时的顺序,后期可以调为随机。
class GA_FSP:
def __init__(self,pc=0.6,pm=0.1,N=20,T=50,n=8,machine=6):
'''
N = 20 #群体大小
T = 50 #Ga终止进化代数
Pc = 0.6 #交叉概率
Pm=0.1 #变异概率
n = 5 #工件个数
machine = 4 #机器个数
'''
self.pc = pc
self.pm = pm
self.N = N
self.T = T
self.n = n
self.machine = machine
self.source_data = self.random_init_data(self.n,self.machine)
self.order_list1 = [i for i in range(1,self.n+1)]
random.shuffle(self.order_list1)
def random_init_data(self,m,n):
'''
m 是工件数
n 是机器数
'''
source_data_dict={}
source_data = np.random.randint(1,99,size=[m,n])
source_data_0 = [i for i in range(1,m+1)]
# source_data = np.insert(source_data, 0, values=np.array(source_data_0), axis=1)
source_data = source_data.tolist()
for p in range(len(source_data)):
source_data_dict["{}".format(source_data_0[p])] = source_data[p]
return source_data_dict
def fitness(self,order_list):
'''
输入状态数据,即可输出适应度和加工时间
'''
def init_group(order_list):
'''
根据列表顺序调整整体数据。
'''
three_init_data = copy.deepcopy(order_list)
# print(three_init_data)
# print(order_list)
# print(self.source_data)
for j in range(len(order_list)):
i = order_list[j]
order_i = str(i)
init_i = i-1
three_init_data[j] = self.source_data[order_i]
init_group = np.array(three_init_data)
return init_group
init_data = init_group(order_list)
# k为加工工件数量
# j为机器数量
k = init_data.shape[0]
j = init_data.shape[1]-1
def find_time(k,j):
if k==1 and j==1:
return init_data[0,1]
elif k>=2 and j==1:
return find_time(k-1,1)+init_data[k-1,1]
elif k == 1 and j>=2:
return find_time(1,j-1)+init_data[0,j]
else:
time = max(find_time(k-1,j),find_time(k,j-1))+init_data[k-1,j]
return time
time = find_time(k,j)
fitness = 1/time
return fitness,time
def init_list(self,N):
N_group = []
for i in range(N):
order_list = [p for p in range(1,self.n+1)]
random.shuffle(order_list)
# node = Node(order_list,fitness(order_list))
N_group.append(order_list)
return N_group
def where_change(self,three_change_list):
for i in range(len(three_change_list)):
if three_change_list[i]<=0:
return i
continue
def cross_change(self,choice_three):
changed_list = []
'''
函数:两点交叉 传入数据为node列表 长度20
'''
def change_main(a1,a2):
'''
两点交叉
传入数据为两个个体
'''
# print("两个父类")
# print(a1.data)
# print(a2.data)
a1_copy = copy.deepcopy(a1)
a2_copy = copy.deepcopy(a2)
random_1=random.randint(0,self.n-1)
random_2=random.randint(0,self.n-1)
while random_1 > random_2 or random_1 == random_2:
random_1 = random.randint(0,self.n-1)
random_2 = random.randint(0,self.n-1)
a1_copy.data[random_1:random_2],a2_copy.data[random_1:random_2]=a2_copy.data[random_1:random_2],a1_copy.data[random_1:random_2]
intermediate1 = a1.data[random_1:random_2]
intermediate2 = a2.data[random_1:random_2]
# print("两个随机数")
# print(random_1)
# print(random_2)
head1 = []
head2 = []
tail1 = []
tail2 = []
# 子代1头
# print("head1")
for i in a1_copy.data[:random_1]:
# print(i)
while i in intermediate2:
i = intermediate1[intermediate2.index(i)]
head1.append(i)
# 子代1尾
# print("tail1")
for i in a1_copy.data[random_2:]:
while i in intermediate2:
i = intermediate1[intermediate2.index(i)]
tail1.append(i)
# 子代2头
# print("head2")
for i in a2_copy.data[:random_1]:
while i in intermediate1:
i = intermediate2[intermediate1.index(i)]
head2.append(i)
# 子代2尾
# print("tail2")
for i in a2_copy.data[random_2:]:
while i in intermediate1:
i = intermediate2[intermediate1.index(i)]
tail2.append(i)
result1 = head1 + intermediate2 + tail1
result2 = head2 + intermediate1 + tail2
# print("两个子类")
# print(result1)
# print(result2)
result1 = Node(result1,self.fitness(result1))
result2 = Node(result2,self.fitness(result2))
return result1,result2
male = choice_three[0:len(choice_three):2]
female = choice_three[1:len(choice_three):2]
if len(male) != len(female):
minmale = min(len(male),len(female))
else:
minmale = len(male)
for j in range(minmale):
cross_rand = random.random()
if cross_rand<self.pc:
a1,a2 = change_main(male[j],female[j])
changed_list.append(a1)
changed_list.append(a2)
else:
changed_list.append(male[j])
changed_list.append(female[j])
# print(changed_list)
return changed_list
def variation_change(self,cross_change_three):
changed_variation=[]
for j in range(len(cross_change_three)):
variation_rand = random.random()
if variation_rand<self.pm:
random_1=random.randint(0,self.n-1)
random_2=random.randint(0,self.n-1)
# print("变异之前的数据",cross_change_three[j].data)
cross_change_three[j].data[random_1],cross_change_three[j].data[random_2]=cross_change_three[j].data[random_2],cross_change_three[j].data[random_1]
changed_variation.append(cross_change_three[j])
# print("变异之后的数据",cross_change_three[j].data)
else:
changed_variation.append(cross_change_three[j])
return changed_variation
def __sort_threelist(self,arr):
'''
冒泡排序
'''
n = len(arr)
for i in range(n):
for j in range(0,n-i-1):
if arr[j].fitness[1]>arr[j+1].fitness[1]:
arr[j],arr[j+1] = arr[j+1] ,arr[j]
return arr
def pro_go(self):
#初始化种群
N_group = self.init_list(self.N)
for j in range(len(N_group)):
N_group[j] = Node(N_group[j],self.fitness(N_group[j]))
#开始迭代
draw_x = []
draw_y = []
for num in range(self.T+1):
#计算适应度
for j in range(len(N_group)):
N_group[j].fitness = self.fitness(N_group[j].data)
# print(N_group[j].fitness[0])
# print(N_group)
if num>self.T:
break
#选择操作
choiceList = [N_group[i].fitness[1] for i in range(len(N_group))]
# print(choiceList)
'''
求出个体i被选中的概率
'''
choice = copy.deepcopy(choiceList)
choice = [choiceList[j]/sum(choiceList) for j in range(len(choiceList))]
# print(choice)
sum_choice = []
for p in range(len(choice)):
if p == 0 :
sum_choice.append(choice[p])
else:
sum_choice.append(choice[p] + sum_choice[p-1])
# print(sum_choice)
# print(rand_num)
'''
选数 原先列表有几行就要选出几个
'''
choice_three = []
for i in range(self.N):
rand_num = random.random()
three_change_list = [rand_num-sum_choice[i] for i in range(len(sum_choice))]
change_index = self.where_change(three_change_list)
choice_three.append(N_group[change_index])
'''
交叉
传入种群节点即可
'''
cross_choice_three = self.cross_change(choice_three)
# print(choice_three)
'''
变异
'''
variation_three = self.variation_change(cross_choice_three)
sort_group= N_group+variation_three
# print("第"+str(num)+"次迭代",end=" ")
N_group = self.__sort_threelist(sort_group)[0:self.N]
# print(N_group[0].fitness[1],end='--')
# print(N_group[-1].fitness[1])
# print()
draw_x.append(num)
draw_y.append(N_group[0].fitness[1])
random.shuffle(N_group)
N_group = self.__sort_threelist(sort_group)[0:self.N]
# for i in range(len(N_group)):
# print(N_group[i].data)
return N_group[i],draw_x,draw_y
'''
n为工件数
m为机器数
'''
testlist = [[8,6,200],[10,8,100],[12,10,60]]
for test in testlist:
n = test[0]
m = test[1]
T = test[2]
Ga1 = GA_FSP(n=n,machine=m,T=T)
best1,draw_x,draw_y = Ga1.pro_go()
final_data = best1.data
plt.plot(draw_x, draw_y)
plt.title('工件为{},机器数为{}'.format(n,m))
plt.show()