FJSP-GA/GA.py

164 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import itertools
import random
import numpy as np
from Decode import Decode
from Instance import *
class GA():
def __init__(self):
self.Pop_size = 500 # 种群数量
self.Pc = 0.8 # 交叉概率
self.Pm = 0.15 # 降低变异概率到0.15
self.Pv = 0.5 # 选择何种方式进行交叉的概率阈值
self.Pw = 0.95 # 选择何种方式进行变异的概率阈值
self.Max_Itertions = 100
# 适应度
def fitness(self, CHS, J, Processing_time, M_num, Len):
Fit = []
for i in range(len(CHS)):
d = Decode(J, Processing_time, M_num) # 实例化一个解码器,传入问题参数
Fit.append(d.decode(CHS[i], Len))
return Fit
def pmx_crossover(self, parent1, parent2, length):
if length <= 1:
return parent1.copy(), parent2.copy()
# 确保交叉区间[a, b]有效a < b
a = random.randint(0, length - 2)
b = random.randint(a + 1, length - 1)
child1 = parent1.copy()
child2 = parent2.copy()
# 建立映射表parent1[a:b+1]与parent2[a:b+1]的对应关系)
map1 = {parent1[i]: parent2[i] for i in range(a, b + 1)} # p1→p2的映射
map2 = {parent2[i]: parent1[i] for i in range(a, b + 1)} # p2→p1的映射
# 处理child1交叉区间外的元素替换
for i in range(length):
if i < a or i > b:
val = child1[i]
# 限制循环次数避免无限循环最多循环len(map1)次)
loop_count = 0
max_loop = len(map1)
while val in map1 and map1[val] in parent1[a:b + 1] and loop_count < max_loop:
val = map1[val]
loop_count += 1
child1[i] = map1.get(val, val) # 若超出循环次数直接使用当前val
# 处理child2交叉区间外的元素替换
for i in range(length):
if i < a or i > b:
val = child2[i]
loop_count = 0
max_loop = len(map2)
while val in map2 and map2[val] in parent2[a:b + 1] and loop_count < max_loop:
val = map2[val]
loop_count += 1
child2[i] = map2.get(val, val)
return child1, child2
# 机器部分交叉
def machine_cross(self, CHS1, CHS2, T0):
"""
:param CHS1: 基因1
:param CHS2: 基因2
:param T0: 工序总数
:return: 交叉后的基因
"""
OS1, MS1 = CHS1[:T0], CHS1[T0:]
OS2, MS2 = CHS2[:T0], CHS2[T0:]
# 随机选择交叉位置
T_r = list(range(T0))
random.shuffle(T_r)
r = random.randint(1, T0 // 2) # 交叉部分长度
R = T_r[:r]
# 交换机器选择部分
for i in R:
MS1[i], MS2[i] = MS2[i], MS1[i]
return np.hstack((OS1, MS1)), np.hstack((OS2, MS2))
# 工序部分交叉使用PMX交叉
def operation_cross(self, CHS1, CHS2, T0, J_num):
OS1 = list(CHS1[T0:2 * T0]) # 确保转换为列表
OS2 = list(CHS2[T0:2 * T0])
MS1 = CHS1[0:T0].copy()
MS2 = CHS2[0:T0].copy()
# 调用修复后的PMX交叉
new_os1, new_os2 = self.pmx_crossover(OS1, OS2, T0)
CHS1 = np.hstack((MS1, new_os1))
CHS2 = np.hstack((MS2, new_os2))
return CHS1, CHS2
# 机器部分变异(仅在可用机器集中选择)
def machine_variation(self, CHS, O, T0, J):
"""
:param CHS: 基因
:param O: 加工时间矩阵
:param T0: 工序总数
:param J: 各工件加工信息
:return: 变异后的基因
"""
OS = CHS[:T0]
MS = CHS[T0:]
# 确定变异位置数量
r = random.randint(1, max(1, T0 // 10)) # 最多变异10%的位置
positions = random.sample(range(T0), r)
for pos in positions:
# 找到该位置对应的工件和工序
site = 0
job = -1
op = -1
for k, v in J.items():
if site + v > pos:
job = k - 1 # 转换为0索引
op = pos - site
break
site += v
# 获取该工序的可用机器
D = O[job][op]
available_machines = [k for k, val in enumerate(D) if val != 9999]
if len(available_machines) <= 1:
continue # 只有一个可用机器时不变异
# 随机选择一个不同的可用机器
current_idx = MS[pos]
current_machine = available_machines[current_idx]
new_machine = random.choice([m for m in available_machines if m != current_machine])
MS[pos] = available_machines.index(new_machine)
return np.hstack((OS, MS))
# 工序部分变异
def operation_variation(self, CHS, T0, J_num, J, O, M_num):
"""
:param CHS: 基因
:param T0: 工序总数
:param J_num: 工件总数
:param J: 各工件加工信息
:param O: 加工时间矩阵
:param M_num: 机器总数
:return: 变异后的基因
"""
OS = list(CHS[:T0])
MS = CHS[T0:]
# 随机选择两个位置交换
i, j = random.sample(range(T0), 2)
OS[i], OS[j] = OS[j], OS[i]
return np.hstack((OS, MS))