数学建模学习(111):改进遗传算法(引入模拟退火、轮盘赌和网格搜索)求解JSP问题

news2024/9/20 16:26:56

文章目录

  • 一、车间调度问题
    • 1.1目前处理方法
    • 1.2简单案例
  • 二、基于改进遗传算法求解车间调度
    • 2.1车间调度背景介绍
    • 2.2遗传算法介绍
      • 2.2.1基本流程
      • 2.2.2遗传算法的基本操作和公式
      • 2.2.3遗传算法的优势
      • 2.2.4遗传算法的不足
    • 2.3讲解本文思路及代码
    • 2.4算法执行结果:
  • 三、本文代码创新点
  • 四、附上完整代码

一、车间调度问题

  车间作业调度问题(JSP)是生产调度领域的经典问题之一,广泛应用于制造业、物流等领域。在一个典型的车间中,多个工件需要在多台机器上按特定的顺序进行加工。每个工件的加工步骤及其顺序是预先确定的,不同工件的加工顺序可能不同。 车间作业调度问题的目标是确定工件在各机器上的加工顺序,以最小化所有工件的完成总时间(即 makespan)。

1.1目前处理方法

  车间作业调度问题因其高度的复杂性和计算上的挑战性,被归类为NP难问题。为了解决这一问题,学术界和工业界已经提出了多种方法,涵盖了精确算法、启发式算法以及元启发式算法。
  精确算法,如分支定界法和动态规划,理论上能够找到全局最优解,但由于其高计算复杂度,通常不适用于大规模问题。
  启发式算法,例如优先规则,如最短加工时间优先(SPT)和最早截止时间优先(EDD),通过简单的规则快速生成可行解。尽管这些方法计算速度快,但解的质量往往无法得到保证。
  元启发式算法,包括遗传算法(GA)、模拟退火(SA)和粒子群优化(PSO),在处理复杂优化问题时表现出色。遗传算法通过模拟自然选择和遗传机制来不断优化解的种群;模拟退火算法通过模拟物质冷却过程中的能量变化来避免局部最优;粒子群优化算法则通过模拟鸟群觅食行为在多维空间中搜索全局最优解。
  近年来,混合算法也逐渐受到关注,例如结合遗传算法和禁忌算法的混合算法,能够利用不同算法的优势,提高解的质量和求解效率。

1.2简单案例

  我们假设有三个机器(M1, M2, M3)和三个工件(J1, J2, J3),每个工件已有不同的加工步骤和时间。这是一个简单的示例:
  假设加工步骤和时间如下:
  J1: M1(3) → M2(2) → M3(2)
  J2: M2(4) → M3(1) → M1(3)
  J3: M3(2) → M1(4) → M2(3)
  以时间为x轴,每个机器为y轴,利用Python求解,每种工件代表1种颜色,可绘制甘特图如下所示:
请添加图片描述
  绘图相应代码如下:

import matplotlib.pyplot as plt
import random
plt.rcParams['font.sans-serif'] = ['SimHei']
# 创建一个表示任务的列表,每个任务用一个字典表示
tasks = [
    {"Task": "J1", "Machine": "M1", "Start": 0, "Finish": 3},
    {"Task": "J1", "Machine": "M2", "Start": 3, "Finish": 5},
    {"Task": "J1", "Machine": "M3", "Start": 5, "Finish": 7},
    {"Task": "J2", "Machine": "M2", "Start": 0, "Finish": 4},
    {"Task": "J2", "Machine": "M3", "Start": 4, "Finish": 5},
    {"Task": "J2", "Machine": "M1", "Start": 5, "Finish": 8},
    {"Task": "J3", "Machine": "M3", "Start": 0, "Finish": 2},
    {"Task": "J3", "Machine": "M1", "Start": 2, "Finish": 6},
    {"Task": "J3", "Machine": "M2", "Start": 6, "Finish": 9},
]
# 创建一个新的图形
fig, gnt = plt.subplots()

# 设置Y轴
gnt.set_ylim(0, 40)
gnt.set_xlim(0, 12)
# 设置Y轴标签
gnt.set_yticks([10, 20, 30])
gnt.set_yticklabels(['M1', 'M2', 'M3'])
# 设置X轴标签
gnt.set_xlabel('时间')
gnt.set_ylabel('机器')
# 生成不同的颜色,每个工件一个颜色
task_colors = {}
for task in tasks:
    if task["Task"] not in task_colors:
        task_colors[task["Task"]] = (random.random(), random.random(), random.random())
# 绘制任务
for task in tasks:
    machine_map = {"M1": 10, "M2": 20, "M3": 30}
    start = task["Start"]
    finish = task["Finish"]
    machine = machine_map[task["Machine"]]
    color = task_colors[task["Task"]]
    gnt.broken_barh([(start, finish - start)], (machine - 5, 9), facecolors=color)
# 显示甘特图
plt.show()

二、基于改进遗传算法求解车间调度

2.1车间调度背景介绍

  在一个车间内,有10台机器,每台机器负责一道加工步骤。这些机器需要完成10个工件的加工,每个工件都有10个加工步骤。不同工件的加工步骤顺序各不相同且顺序不可更改。每个加工步骤由对应的机器在一定时间内完成。你需要确定各个工件在不同机器上的加工顺序,以最小化所有工件完成加工所需的总时间。具体的不同工件加工顺序与加工时长如下表所示:
在这里插入图片描述
  对于每个工件,其加工顺序和每台机器上的加工耗时一定,拿第一行举例。工件1的加工顺序为:M1-M2-M4-M3-M5-M6-M8-M4-M7-M10。其花费时间固定,分别为28-33-11-48-18-19-86-64-65-90。我们要做的就是对机器进行调度,什么时候加工第几个工件的第几个工序

2.2遗传算法介绍

  遗传算法(Genetic Algorithm,GA)是一类模拟自然选择和遗传机制的进化算法,主要用于优化和搜索问题。它最早由约翰·霍兰德(John Holland)在20世纪70年代提出。遗传算法通过模拟自然界生物进化的过程,包括选择、交叉(杂交)和变异等操作,逐步优化问题的解。

2.2.1基本流程

  1. 初始化种群:随机生成一定数量的初始解,称为个体,这些个体构成初始种群。
  2. 适应度评估:计算每个个体的适应度值,适应度值越高表示该个体越适合问题的求解。
  3. 选择:根据个体的适应度值,选择一些个体作为父代。常用的选择方法有轮盘赌选择、锦标赛选择等。
  4. 交叉:通过交叉操作(即杂交)生成新的个体,交叉操作将两个父代个体的部分基因交换,从而生成子代个体。
  5. 变异:对新生成的个体进行变异操作,即随机改变个体的一部分基因,以增加种群的多样性。
  6. 生成新种群:用子代个体替换部分或全部父代个体,形成新一代种群。
  7. 重复迭代:重复上述步骤,直到满足终止条件,如达到最大迭代次数或种群中的最佳适应度值达到预期目标。

2.2.2遗传算法的基本操作和公式

  1. 选择操作:选择操作通过适应度值选择个体。轮盘赌选择法的概率计算公式:
    P i = f i ∑ j = 1 N f j P_i = \frac{f_i}{\sum_{j=1}^{N} f_j} Pi=j=1Nfjfi
    其中,(P_i) 是第 (i) 个个体被选中的概率,(f_i) 是第 (i) 个个体的适应度值,(N) 是种群的大小。
  2. 交叉操作:交叉操作将两个父代个体的基因部分交换,生成两个新的个体。单点交叉的公式:
    子代1 = ( 父代1 1 , 父代1 2 , … , 父代1 c , 父代2 c + 1 , … , 父代2 n ) 子代2 = ( 父代2 1 , 父代2 2 , … , 父代2 c , 父代1 c + 1 , … , 父代1 n ) \begin{aligned} &\text{子代1} = (\text{父代1}_1, \text{父代1}_2, \ldots, \text{父代1}_c, \text{父代2}_{c+1}, \ldots, \text{父代2}_n) \\ &\text{子代2} = (\text{父代2}_1, \text{父代2}_2, \ldots, \text{父代2}_c, \text{父代1}_{c+1}, \ldots, \text{父代1}_n) \end{aligned} 子代1=(父代11,父代12,,父代1c,父代2c+1,,父代2n)子代2=(父代21,父代22,,父代2c,父代1c+1,,父代1n)
    其中,(c) 是交叉点的位置,(\text{父代1}) 和 (\text{父代2}) 是两个父代个体,(n) 是个体的基因长度。
  3. 变异操作:变异操作随机改变个体的一部分基因。对于一个基因序列 (x = (x_1, x_2, \ldots, x_n)),其变异公式可以表示为:
    x i ′ = { 随机值 如果 随机概率 < 变异概率 x i 否则 x_i' = \begin{cases} \text{随机值} & \text{如果 } \text{随机概率} < \text{变异概率} \\ x_i & \text{否则} \end{cases} xi={随机值xi如果 随机概率<变异概率否则
    其中,(x_i’) 是变异后的基因值,变异概率通常设为一个小值,如0.01或0.1。

2.2.3遗传算法的优势

  • 全局搜索能力强:遗传算法通过选择、交叉和变异等操作,可以在较大范围内进行搜索,能够较好地避免陷入局部最优。
  • 适用范围广:遗传算法适用于各种优化问题,包括离散问题和连续问题。
  • 易于并行化:由于种群中的个体可以并行计算适应度值,遗传算法易于实现并行计算,提升计算效率。

2.2.4遗传算法的不足

  • 计算开销大:遗传算法需要多次迭代,且每次迭代需要计算多个个体的适应度值,计算开销较大。
  • 参数选择复杂:遗传算法的性能对参数(如种群大小、交叉率、变异率等)依赖较大,参数选择不当可能影响算法效果。
  • 收敛速度慢:在某些情况下,遗传算法的收敛速度较慢,可能需要较多的迭代次数才能找到较优的解。

2.3讲解本文思路及代码

  本文改进遗传算法的代码更侧重于结合多种优化算法解决问题。利用网格搜索找到最优参数,并且在搜索过程中结合模拟退火算法进行局部优化,探索了更大范围的解空间,寻找最优解的可能性更大,利用上述显著优势尝试求解JSP问题。
  Step1:初始化。创建一个初始种群,每个个体代表一个可能的作业顺序。通过随机生成的方式确保种群的多样性。

def createPop(Jm, popSize):
    pop = []
    for i in range(popSize):
        pop.append(createInd(Jm))
    return pop

  Step2:选择。使用轮盘赌选择方法,根据个体的适应度(即完工时间)概率性地选择个体。适应度越高(即完工时间越短),被选择的概率越大。

def roulette_wheel_selection(fitness):
    total_fitness = sum(fitness)
    pick = random.uniform(0, total_fitness)
    current = 0
    for i, fit in enumerate(fitness):
        current += fit
        if current > pick:
            return i

  Step3:交叉。应用自定义的交叉操作,将父代个体组合生成新的后代。通过选择两个父代个体,交换它们的一部分基因片段,以继承父母双方的特征。

def cross(A, B):
    n = len(A)
    r1 = np.random.randint(n)
    r2 = np.random.randint(n)
    rl, rr = min(r1, r2), max(r1, r2)
    if rl == rr:
        return A, B
    bt = copy.deepcopy(B)
    afinal = copy.deepcopy(A)
    for i in range(rl, rr + 1):
        bt.remove(A[i])
    k = 0
    for i in range(n):
        if i < rl or i > rr:
            afinal[i] = bt[k]
            k += 1
    at = copy.deepcopy(A)
    bfinal = copy.deepcopy(B)
    for i in range(rl, rr + 1):
        at.remove(B[i])
    k = 0
    for i in range(n):
        if i < rl or i > rr:
            bfinal[i] = at[k]
            k += 1
    return afinal, bfinal

  Step4:变异。通过随机交换作业操作引入变异,以保持基因多样性。变异操作有助于防止算法陷入局部最优。

def mutate(individual, mutation_rate):
    if random.random() < mutation_rate:
        index1, index2 = random.sample(range(len(individual)), 2)
        individual[index1], individual[index2] = individual[index2], individual[index1]
    return individual

  Step5:解码。使用详细的调度算法解码每个个体,以评估其完工时间,确保所有作业约束得到满足。解码过程包括为每个作业在特定机器上安排开始和结束时间。

def decode(J, P, s):
    n, m = J.shape
    T = [[[0]] for _ in range(m)]
    C = np.zeros((n, m))
    k = np.zeros(n, dtype=int)
    for job in s:
        if job < 0 or job >= n:
            print(f"Invalid job index: {job}")
            continue
        if k[job] < 0 or k[job] >= m:
            print(f"Invalid operation index for job {job}: {k[job]}")
            continue
        machine = J[job, k[job]] - 1
        process_time = P[job, k[job]]
        last_job_finish = C[job, k[job] - 1] if k[job] > 0 else 0
        start_time = max(last_job_finish, T[machine][-1][-1])
        insert_index = len(T[machine])
        for i in range(1, len(T[machine])):
            gap_start = max(last_job_finish, T[machine][i - 1][-1])
            gap_end = T[machine][i][0]
            if gap_end - gap_start >= process_time:
                start_time = gap_start
                insert_index = i
                break
        end_time = start_time + process_time
        C[job, k[job]] = end_time
        T[machine].insert(insert_index, [start_time, job, k[job], end_time])
        k[job] += 1
    M = [[] for _ in range(m)]
    for machine in range(m):
        for j in T[machine][1:]:
            M[machine].append(j[1])
    return T, M, C

  Step6:模拟退火。为了增强搜索能力,本文结合模拟退火方法,对个体解进行微调,允许偶尔接受较差解,以跳出局部最优。模拟退火通过**逐渐降低“温度”**来减少接受较差解的概率,从而达到全局优化的效果。

def simulated_annealing(individual, J, P, T0, alpha, max_iter):
    current_solution = copy.deepcopy(individual)
    Tmax, _, current_fitness = decode(J, P, current_solution)
    best_solution = copy.deepcopy(current_solution)
    best_fitness = current_fitness.max()
    T = T0
    for i in range(max_iter):
        new_solution = mutate(copy.deepcopy(current_solution), 1.0)
        Tmax, _, new_fitness = decode(J, P, new_solution)
        delta_fitness = new_fitness.max() - current_fitness.max()
        if delta_fitness < 0 or random.random() < math.exp(-delta_fitness / T):
            current_solution = new_solution
            current_fitness = new_fitness
            if current_fitness.max() < best_fitness:
                best_solution = copy.deepcopy(current_solution)
                best_fitness = current_fitness.max()
        T *= alpha
    return best_solution, best_fitness

  Step7:算法执行过程。通过初始化种群并不断执行选择、交叉、变异和解码操作,结合模拟退火优化,遗传算法逐步优化种群中的个体。每一代后,种群会逐步接近最优解,最终输出最优的调度方案。以下是主函数部分,展示了算法的执行过程:

# 主函数
if __name__ == "__main__":
    J, P = load_data('05.txt')
    pop_size = 50
    max_iter = 5000
    w = 0.5
    c1 = 1.5
    c2 = 1.5
    best_position, best_fitness = pso(J, P, pop_size, max_iter, w, c1, c2)
    print(f'Best Solution: {best_position}')
    print(f'Best Fitness: {best_fitness}')
    T, M, C = decode(J, P, best_position)
    drawGantt(T)
    plt.show()

2.4算法执行结果:

  经过长时间运行,本文得到最少用时为 893s, 由网格搜索得到的具体参数如下所示:
在这里插入图片描述

三、本文代码创新点

  • 创新1:引入混合优化算法。本文结合了遗传算法、模拟退火和网格搜索三种优化方法,提升了搜索效率和解的质量。遗传算法利用选择、交叉和变异操作进行全局搜索,模拟退火通过局部搜索进一步优化解,而网格搜索则用于寻找最佳的参数组合。

  • 创新2:采用网格搜索。param_grid定义了一系列参数范围,通过遍历所有参数组合,自动化寻找最佳参数设置,确保算法在参数空间内找到最优解。

  • 创新3:引入模拟退火策略。在遗传算法的基础上,引入模拟退火策略,有效地跳出局部最优解,进一步提升解的质量。通过控制退火过程的温度,平衡了探索与开发。

  • 创新4:自适应交叉和变异操作。交叉和变异操作的实现更加灵活,能够根据个体特性进行调整。变异操作通过概率控制增加了种群的多样性,帮助避免过早收敛。

  • 创新5:甘特图绘制。通过rawGantt函数,使用不同颜色表示不同工件的操作,并在甘特图上显示详细的操作信息,提供了直观的调度结果可视化。

  • 创新6:基于轮盘赌选择。采用轮盘赌选择机制,增加了适应度高的个体被选中的概率,确保了优良基因的传递,提高了算法的收敛速度和效果。

四、附上完整代码

import copy
import numpy as np
import matplotlib.pyplot as plt
import random
import math
#------------------------------------------------------------------------------------------------------------------

#-------------------------------------------------------------------------------------
def createInd(J):
    #创建个体
    n = J.shape[0]
    s = []
    Jm = J.copy()
    while not np.all(Jm == 0):
        I = np.random.randint(0, n)
        M = Jm[I, 0]
        if M != 0:
            s.append(I)
            b = np.roll(Jm[I, :], -1)
            b[-1] = 0
            Jm[I, :] = b
    return s

def createPop(Jm, popSize):
    #创建种群
    pop = []
    for i in range(popSize):
        pop.append(createInd(Jm))
    return pop

def decode(J, P, s):
    #解码
    n, m = J.shape
    T = [[[0]] for _ in range(m)]
    C = np.zeros((n, m))
    k = np.zeros(n, dtype=int)
    for job in s:
        machine = J[job, k[job]] - 1
        process_time = P[job, k[job]]
        last_job_finish = C[job, k[job] - 1] if k[job] > 0 else 0
        start_time = max(last_job_finish, T[machine][-1][-1])
        insert_index = len(T[machine])
        for i in range(1, len(T[machine])):
            gap_start = max(last_job_finish, T[machine][i - 1][-1])
            gap_end = T[machine][i][0]
            if gap_end - gap_start >= process_time:
                start_time = gap_start
                insert_index = i
                break
        end_time = start_time + process_time
        C[job, k[job]] = end_time
        T[machine].insert(insert_index, [start_time, job, k[job], end_time])
        k[job] += 1
    M = [[] for _ in range(m)]
    for machine in range(m):
        for j in T[machine][1:]:
            M[machine].append(j[1])
    return T, M, C

def drawGantt(timelist):
    #绘制甘特图
    T = timelist.copy()
    plt.rcParams['font.sans-serif'] = ['SimHei']
    fig, ax = plt.subplots(figsize=(10, 6))
    color_map = {}
    for machine in T:
        for task_data in machine[1:]:
            job_idx, operation_idx = task_data[1], task_data[2]
            if job_idx not in color_map:
                color_map[job_idx] = (random.random(), random.random(), random.random())
    for machine_idx, machine_schedule in enumerate(T):
        for task_data in machine_schedule[1:]:
            start_time, job_idx, operation_idx, end_time = task_data
            color = color_map[job_idx]
            ax.barh(machine_idx, end_time - start_time, left=start_time, height=0.4, color=color)
            label = f'{job_idx}-{operation_idx}'
            ax.text((start_time + end_time) / 2, machine_idx, label, ha='center', va='center', color='white', fontsize=10)
    ax.set_yticks(range(len(T)))
    ax.set_yticklabels([f'machine{i + 1}' for i in range(len(T))])
    plt.xlabel("时间")
    plt.title("JSP甘特图")
    l = []
    for job_idx, color in color_map.items():
        l.append(plt.Rectangle((0, 0), 1, 1, color=color, label=f'{job_idx}'))
    plt.legend(handles=l, title='工件')

def cross(A, B):
    #交叉操作
    n = len(A)
    r1 = np.random.randint(n)
    r2 = np.random.randint(n)
    rl, rr = min(r1, r2), max(r1, r2)
    if rl == rr:
        return A, B
    bt = copy.deepcopy(B)
    afinal = copy.deepcopy(A)
    for i in range(rl, rr + 1):
        bt.remove(A[i])
    k = 0
    for i in range(n):
        if i < rl or i > rr:
            afinal[i] = bt[k]
            k += 1
    at = copy.deepcopy(A)
    bfinal = copy.deepcopy(B)
    for i in range(rl, rr + 1):
        at.remove(B[i])
    k = 0
    for i in range(n):
        if i < rl or i > rr:
            bfinal[i] = at[k]
            k += 1
    return afinal, bfinal

def load_data(path):
    #载入操作
    with open(path, 'r') as file:
        lines = file.readlines()
    job_num, machines_num = map(int, lines[0].split())
    J = np.zeros((job_num, len(lines[1].split()) // 2), dtype=int)
    P = np.zeros((job_num, len(lines[1].split()) // 2), dtype=int)
    for i in range(1, len(lines)):
        data = list(map(int, lines[i].split()))
        for j in range(len(data)):
            if j % 2 == 0:
                J[i - 1][j // 2] = data[j]
            else:
                P[i - 1][j // 2] = data[j]
    return J, P

def mutate(individual, mutation_rate):
    #变异操作
    if random.random() < mutation_rate:
        index1, index2 = random.sample(range(len(individual)), 2)
        individual[index1], individual[index2] = individual[index2], individual[index1]
    return individual

def roulette_wheel_selection(fitness):
    #轮盘赌
    total_fitness = sum(fitness)
    pick = random.uniform(0, total_fitness)
    current = 0
    for i, fit in enumerate(fitness):
        current += fit
        if current > pick:
            return i

def simulated_annealing(individual, J, P, T0, alpha, max_iter):
    #模拟退火
    current_solution = copy.deepcopy(individual)
    Tmax, _, current_fitness = decode(J, P, current_solution)
    best_solution = copy.deepcopy(current_solution)
    best_fitness = current_fitness.max()
    T = T0
    for i in range(max_iter):
        new_solution = mutate(copy.deepcopy(current_solution), 1.0)
        Tmax, _, new_fitness = decode(J, P, new_solution)
        delta_fitness = new_fitness.max() - current_fitness.max()
        if delta_fitness < 0 or random.random() < math.exp(-delta_fitness / T):
            current_solution = new_solution
            current_fitness = new_fitness
            if current_fitness.max() < best_fitness:
                best_solution = copy.deepcopy(current_solution)
                best_fitness = current_fitness.max()
        T *= alpha
    return best_solution, best_fitness

param_grid = {
    #网格搜索
    'pop_size': [300, 400, 500],
    'mutation_rate': [ 0.5, 0.7, 0.9],
    'T0': [ 1500, 2000],
    'alpha': [0.95, 0.97, 0.99],
    'max_iter': [50, 100, 150, 200]
}

# 加载数据
J, P = load_data('05.txt')
n, m = J.shape

# 初始化最优参数和最优结果
best_params = {}
best_Cmax = float('inf')

# 网格搜索循环
for pop_size in param_grid['pop_size']:
    for mutation_rate in param_grid['mutation_rate']:
        for T0 in param_grid['T0']:
            for alpha in param_grid['alpha']:
                for max_iter in param_grid['max_iter']:
                    print(f'Testing parameters: pop_size={pop_size}, mutation_rate={mutation_rate}, T0={T0}, alpha={alpha}, max_iter={max_iter}')
                    pop = createPop(J, pop_size)
                    Tmax, _, C = decode(J, P, pop[0])
                    fitness = [C.max()]
                    Cmax = C.max()
                    bestInd = copy.deepcopy(pop[0])
                    for i in range(1, pop_size):
                        T_, _, C = decode(J, P, pop[i])
                        if C.max() < Cmax:
                            Tmax = T_
                            Cmax = C.max()
                            bestInd = copy.deepcopy(pop[i])
                        fitness.append(C.max())
                    g = 0
                    gen = n * m
                    while g < gen:
                        g += 1
                        newInd = []
                        newFitness = []
                        l = 0
                        while l < pop_size / 2:
                            l += 1
                            tm = roulette_wheel_selection(fitness)
                            l1, l2 = cross(pop[tm], bestInd)
                            T1, _, C1 = decode(J, P, l1)
                            newInd.append(l1)
                            newFitness.append(C1.max())
                            if C1.max() < Cmax:
                                Tmax = T1
                                Cmax = C1.max()
                                bestInd = copy.deepcopy(l1)
                            T2, _, C2 = decode(J, P, l2)
                            newInd.append(l2)
                            newFitness.append(C2.max())
                            if C2.max() < Cmax:
                                Tmax = T2
                                Cmax = C2.max()
                                bestInd = copy.deepcopy(l2)
                        newpop = pop + newInd
                        newFit = fitness + newFitness
                        newId = np.array(newFit).argsort()[:pop_size]
                        pop = copy.deepcopy([newpop[i] for i in newId])
                        fitness = [newFit[i] for i in newId]
                        for i in range(pop_size):
                            pop[i] = mutate(pop[i], mutation_rate)
                            Ind = copy.deepcopy(pop[i])
                            Tt, _, Ct = decode(J, P, Ind)
                            fitness[i] = Ct.max()
                            if Ct.max() < Cmax:
                                Tmax = Tt
                                Cmax = Ct.max()
                                bestInd = copy.deepcopy(Ind)
                        # 模拟退火
                        for i in range(pop_size):
                            pop[i], fitness[i] = simulated_annealing(pop[i], J, P, T0, alpha, max_iter)
                            if fitness[i] < Cmax:
                                Tmax, _, _ = decode(J, P, pop[i])
                                Cmax = fitness[i]
                                bestInd = copy.deepcopy(pop[i])
                        print(f'第{g}代, Cmax={Cmax}')
                    if Cmax < best_Cmax:
                        best_Cmax = Cmax
                        best_params = {
                            'pop_size': pop_size,
                            'mutation_rate': mutation_rate,
                            'T0': T0,
                            'alpha': alpha,
                            'max_iter': max_iter
                        }

print(f'Best parameters: {best_params}')
print(f'Best Cmax: {best_Cmax}')

# 使用最优参数重新运行算法
pop_size = best_params['pop_size']
mutation_rate = best_params['mutation_rate']
T0 = best_params['T0']
alpha = best_params['alpha']
max_iter = best_params['max_iter']
pop = createPop(J, pop_size)
Tmax, _, C = decode(J, P, pop[0])
fitness = [C.max()]
Cmax = C.max()
bestInd = copy.deepcopy(pop[0])
for i in range(1, pop_size):
    T_, _, C = decode(J, P, pop[i])
    if C.max() < Cmax:
        Tmax = T_
        Cmax = C.max()
        bestInd = copy.deepcopy(pop[i])
    fitness.append(C.max())
g = 0
gen = n * m
chistory = []
while g < gen:
    g += 1
    newInd = []
    newFitness = []
    l = 0
    while l < pop_size / 2:
        l += 1
        tm = roulette_wheel_selection(fitness)
        l1, l2 = cross(pop[tm], bestInd)
        T1, _, C1 = decode(J, P, l1)
        newInd.append(l1)
        newFitness.append(C1.max())
        if C1.max() < Cmax:
            Tmax = T1
            Cmax = C1.max()
            bestInd = copy.deepcopy(l1)
        T2, _, C2 = decode(J, P, l2)
        newInd.append(l2)
        newFitness.append(C2.max())
        if C2.max() < Cmax:
            Tmax = T2
            Cmax = C2.max()
            bestInd = copy.deepcopy(l2)
    newpop = pop + newInd
    newFit = fitness + newFitness
    newId = np.array(newFit).argsort()[:pop_size]
    pop = copy.deepcopy([newpop[i] for i in newId])
    fitness = [newFit[i] for i in newId]
    for i in range(pop_size):
        pop[i] = mutate(pop[i], mutation_rate)
        Ind = copy.deepcopy(pop[i])
        Tt, _, Ct = decode(J, P, Ind)
        fitness[i] = Ct.max()
        if Ct.max() < Cmax:
            Tmax = Tt
            Cmax = Ct.max()
            bestInd = copy.deepcopy(Ind)
    # 模拟退火
    for i in range(pop_size):
        pop[i], fitness[i] = simulated_annealing(pop[i], J, P, T0, alpha, max_iter)
        if fitness[i] < Cmax:
            Tmax, _, _ = decode(J, P, pop[i])
            Cmax = fitness[i]
            bestInd = copy.deepcopy(pop[i])
    print(f'第{g}代, Cmax={Cmax}')
    chistory.append(Cmax)
plt.plot(chistory)
plt.show()
drawGantt(Tmax)
plt.show()


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1937375.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于java的设计模式学习

PS &#xff1a;以作者的亲身来看&#xff0c;这东西对于初学者来说有用但不多&#xff0c;这些东西&#xff0c;更像一种经验的总结&#xff0c;在平时开发当中一般是用不到的&#xff0c;因此站在这个角度上用处不大。 1.工厂模式 1.1 简单工厂模式 我们把new 对象逻辑封装…

SpringBoot缓存注解使用

背景 除了 RedisTemplate 外&#xff0c; 自Spring3.1开始&#xff0c;Spring自带了对缓存的支持。我们可以直接使用Spring缓存技术将某些数据放入本机的缓存中&#xff1b;Spring缓存技术也可以搭配其他缓存中间件(如Redis等)进行使用&#xff0c;将某些数据写入到缓存中间件…

【Linux】信号(signal)

目录 一、信号概念&#xff1a; 二、信号的常见状态&#xff1a; 信号递达&#xff1a; 信号未决&#xff1a; 阻塞信号&#xff1a; 忽略信号&#xff1a; 信号在内核中的表示&#xff1a; 三、信号相关函数&#xff1a; sigset_t &#xff08;类型&#xff09;&…

2024.7.19 作业

1.链表的排序 int list_sort(NodePtr L) {if(NULLL || L->len<1){printf("排序失败");return -1;}int lenL->len1;NodePtr p;int i,j;for( i1;i<len;i){for( j0,pL;j<len-i;j,pp->next){if( p->data > p->next->data ){datatype tp-&…

基于51单片机的步进电机控制系统proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1jFlIJ9I5qxjW8sYKd6vrBQ?pwd9d6q 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMic…

阿里开源的音频模型_原理与实操

英文名称: FunAudioLLM: Voice Understanding and Generation Foundation Models for Natural Interaction Between Humans and LLMs 中文名称: FunAudioLLM: 人与LLMs之间自然互动的语音理解和生成基础模型 论文地址: http://arxiv.org/abs/2407.04051v3 相关论文&#xff1a;…

1、springboot3 vue3开发平台-后端-项目构建

文章目录 1. 创建项目1.1 前置环境条件1.2 项目创建 2. 模块配置2.1 父工程配置概述2.2 配置启动模块2.3 父工程相关依赖管理 1. 创建项目 1.1 前置环境条件 idea2023, jdk17 1.2 项目创建 创建父工程并删除不需要的文件目录&#xff1a; 右键父工程依次创建其他模块 最…

Java | Leetcode Java题解之第260题只出现一次的数字III

题目&#xff1a; 题解&#xff1a; class Solution {public int[] singleNumber(int[] nums) {int xorsum 0;for (int num : nums) {xorsum ^ num;}// 防止溢出int lsb (xorsum Integer.MIN_VALUE ? xorsum : xorsum & (-xorsum));int type1 0, type2 0;for (int n…

vue2.0结合使用 el-scrollbar 和 v-for实现一个横向滚动的元素列表,并且能够自动滚动到指定元素(开箱即用)

效果图&#xff1a; 代码&#xff1a; <div class"gas-mode-item-body"><el-scrollbar style"width: 300px;height: 100%;" wrap-style"overflow-y:hidden" ref"scrollbarRef"><div style"display: flex&quo…

python-最小公倍数(PythonTip)

[题目描述] 编写一个程序&#xff0c;找出能被从1到给定数字n&#xff08;包括n&#xff09;的所有数字整除的最小正数(即最小公倍数)。 定义函数smallest_multiple()的函数&#xff0c;参数为n。 在函数内&#xff0c;返回能被从1到给定数字n&#xff08;包括n&#xff09;的…

珈和科技完成全国首个农险服务类数据产品入表,实现数据资产化

近日&#xff0c;珈和科技与东湖大数据合作&#xff0c;完成全国首个保险服务类数据产品入表&#xff0c;标志着我国商业卫星遥感应用领域迈出了数据资产化的关键一步。 此次入表的数据产品为“华北农业保险服务数据集数据产品”&#xff0c;是珈和科技融合卫星遥感与无人机等…

数据结构----栈

前言 Hello&#xff0c;小伙伴们&#xff0c;今天我们继续数据结构的学习&#xff0c;前面我们学习了顺序表和链表的实现&#xff0c;今天的栈知识也是和前面的知识相辅相成。 如果你喜欢我的内容的话&#xff0c;就请不要吝啬自己手中的三连哟&#xff0c;万分感谢&#xff…

# Redis 入门到精通(七)-- redis 删除策略

Redis 入门到精通&#xff08;七&#xff09;-- redis 删除策略 一、redis 删除策略–过期数据的概念 1、Redis 中的数据特征 Redis 是一种内存级数据库&#xff0c;所有数据均存放在内存中&#xff0c;内存中的数据可以通过TTL指令获取其状态。 XX &#xff1a;具有时效性…

Android Studio引入ndk编译的so库, 通过jni给Java程序使用

前言 工作要求将一个C老项目的函数用ndk打包成库给安卓同事的java程序调用。 这个任务我debuff拉满&#xff1a; 自己之前从来没接触过安卓开发&#xff0c;问了老板为什么不让安卓开发来干&#xff0c;老板说安卓开发不懂c&#xff0c;公司就我一个是懂c的。。。项目开发年…

【STM32嵌入式系统设计与开发---拓展】——1_10矩阵按键

这里写目录标题 1、矩阵按键2、代码片段分析 1、矩阵按键 通过将4x4矩阵按键的每一行依次设为低电平&#xff0c;同时保持其它行为高电平&#xff0c;然后读取所有列的电平状态&#xff0c;可以检测到哪个按键被按下。如果某列变为低电平&#xff0c;说明对应行和列的按键被按下…

day2 单机并发缓存

文章目录 1 sync.Mutex2 支持并发读写3 主体结构 Group3.1 回调 Getter3.2 Group 的定义3.3 Group 的 Get 方法 4 测试 本文代码地址&#xff1a; https://gitee.com/lymgoforIT/gee-cache/tree/master/day2-single-node 本文是7天用Go从零实现分布式缓存GeeCache的第二篇。 …

go 实现websocket以及详细设计流程过程,确保通俗易懂

websocket简介&#xff1a; WebSocket 是一种网络传输协议&#xff0c;可在单个 TCP 连接上进行全双工通信&#xff0c;位于 OSI 模型的应用层。WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455&#xff0c;后由 RFC 7936 补充规范。 WebSocket 使得客户端和服务器之间的数…

昇思学习打卡-21-生成式/Diffusion扩散模型

文章目录 Diffusion扩散模型介绍模型推理结果 Diffusion扩散模型介绍 关于扩散模型&#xff08;Diffusion Models&#xff09;有很多种理解&#xff0c;除了本文介绍的离散时间视角外&#xff0c;还有连续时间视角、概率分布转换视角、马尔可夫链视角、能量函数视角、数据增强…

《样式设计003:布局-自定义view模块》

描述&#xff1a;在开发小程序过程中&#xff0c;发现一些不错的案例&#xff0c;平时使用也比较多&#xff0c;稍微总结了下经验&#xff0c;以下内容可以直接复制使用&#xff0c;希望对大家有所帮助&#xff0c;废话不多说直接上干货&#xff01; 一、布局-自定义view模块 …

el-popover嵌套select弹窗点击实现自定义关闭

需求 el-popover弹窗内嵌套下拉选择框&#xff0c;点击el-popover弹出外部区域需关闭弹窗&#xff0c;点击查询、重置需关闭弹窗&#xff0c; 实现 根据需求要自定义弹窗的关闭和显示&#xff0c;首先想到的是visible属性&#xff0c;在实现过程中经过反复的测验&#xff0…