遗传算法求解问题(1)

news2025/1/11 22:44:47

问题描述

在一个长度为 n 的数组中选择 10 个元素,使得 10个元素的和 与 该元组中元素总和的 1/10 接近

问题约束

数组长度 n:10 <= n <= 100;
数组中没有重复的数字,所以选择的10个元素中也没有重复的数字

遗传算法原理

请移步我上一篇的博客 基于遗传算法解决流水车间调度问题

构建该问题遗传算法模型

染色体编码

每个染色体编码成随机选择的10个元素序列,比如 [1,2,3,4,5,6,7,0,8,9]

基因交换、变异

在遗传算法中,可以将问题的一个可行解看成一条染色体,染色体在配对时发生基因交换或者发生基因变异生成新的染色体,即新的可行解。

而新产生的可行解(染色体)参与到下一次基因配对中,产生另一些新的可行解。

基因交换

'''
如果交换前的解为
【1 2 3 4 5】 解1
【4 2 5 1 3】 解2

要交换部分为
【2 3 4】
【2 5 1】

交换后解为
【1 2 5 1 5】 解3
【4 2 3 4 3】 解4

我们发现此时两个解中的元素有重复,需要进一步约束
下面就是进一步约束
此时 解1 的 头(head) 为 【1】,被交换过来的部分为 【2 5 1】,交换给解2的部分为 【2 3 4】,其中 1 重复,所以将重复的 解1 的 头(head) 更改为 被交换过来的部分1和交换过去的部分同一位置的数,所以 解3 的头(head) 为 4
解1 的 尾(tail)也一样,改为 3
所以最后的 解3 为 【4 2 5 1 3】
          解4 为 【1 2 3 4 5】
'''

基因突变

# 基因突变,从 长度为 n 的数组中随机挑选一个选择替换可行解中的任意一个元素,需要保证替换的元素不能和已有元素重复
# 如:原来的解为 【1,2,3,4,5】,基因突变后 【1,2,17,4,5】

适应度

由于问题目标是:10个元素的和 与 该元组中元素总和的 1/10 接近。

所以在该问题中可行解中10个元素的和 与 该数组中元素总和的 1/10 越接近适应度就越高,即差值的绝对值越小,适应度越高,有适应度公式
适 应 度 = 1 / a b s ( 10 个 元 素 和 − 所 有 元 素 和 / 10 ) 适应度 = 1/ abs(10个元素和-所有元素和/10) =1/abs(10/10)

注意:
有可能随机到的可行解和总和的 1/10 正好相等,但 1/0 会报错,考虑到 是 1/10,所以如果差值不为 0,那么最小的差值是 0.1,即适应度为 10,很显然相等的情况适应度最高,所以将其适应值设置为 11

适应度映射为概率之后的归一化处理

适应度映射为概率

适 应 度 / 适 应 度 的 总 和 = 计 算 映 射 后 的 概 率 适应度 / 适应度的总和=计算映射后的概率 /=

归一化处理

伪代码如下:

此时概率列表为 [0.2, 0.3, 0.7, 1.0]
概率 = random.random()
for i in 列表:
	if 概率 < i:
        选择 i 对应的染色体
        break 退出循环
        
'''
解释
如果此时随机的概率为 0.1,那么 0.1<0.2,选择 0.2 所对应的染色体

如果此时随机的概率为 0.5,那么 0.5>0.2, 0.5>0.3, 0.5<0.7, 选择 0.7 所对应的染色体——在判断 0.5和0.7的关系之前,循环已经判断了 0.5 和 0.2,0.3 之间的大小关系,所以在判断0.5 和0.7的关系时,我们已经保证了此时 随机的概率(0.5)不在0.2和0.3的概率范围之内
'''

具体代码如下

P_guiyi_list = [P_list[0]] + [P_list[i] for i in range(1, len(P_list))] # 归一之后的概率列表

for i in range(N):
    P_num = random.random()
    for j in range(len(P_guiyi_list)):
        if P_num-j < 0:
            finally_data.append(init_data[j])
            break

染色体配对(复制)

遗传算法配对选择的方式多种多样,对该问题我们选择轮盘赌法进行染色体选择。

  • 轮盘赌法——将个体适应度大小映射为概率进行复制:适应度高的个体有更大概率复制,且复制的分数越多。
    1. 将适应度大小映射为概率——比如此时有20个可行解,那么就有20个适应度(比如说 1/路程 ),那么每个可行解映射后的概率为 该可行解的适应度 / 20个可行解的总和
    2. 轮盘赌法的详细介绍(个人理解)—— 比如一开始问题的可行解有20个,即20个染色体,在第一次俩俩配对后生成 N 个可行解,此时问题的可行解有 20+N 个(其中可能有重复的可行解),然后对这 20+N 个可行解的适应度进行降序排序,选择 20+N 个可行解中适应度前 20 的可行解进行下一次染色体配对

遗传算法代码

随机生成长度为 n 的数组

import random

num_list_len = random.randint(10,100)

num_list = []

for i in range(num_list_len):
    num = random.randint(1, 500)
    while num in num_list:
        num = random.randint(1, 500)
    num_list.append(num)

# 长度为 n:num_list_len
# 随机生成的数组:num_list

参数介绍

N = 50 # 种群规模
T = 200 # 迭代次数
PM = 0.05 # 变异概率
PC = 0.7 # 交换概率
num_list_len # 数组长度
num_list # 数组列表

初始化染色体(可行解)

def init(N):
    """
    初始化染色体
    :param N:
    :return:
    """
    init_data = []

    for j in range(N):
        init_list = []
        for i in range(10):
            rand_num = random.randint(0, num_list_len-1)
            while num_list[rand_num] in init_list:
                rand_num = random.randint(0, num_list_len-1)
            init_list.append(num_list[rand_num])
        init_data.append(init_list)

    return init_data

计算适应度

def fitness(init_data):
    """
    计算适应度
    :param init_data: --> list
    :param N: --> num
    :return:
    """
    fit_list = []
    for i in range(len(init_data)):
        sum_num = abs(sum(init_data[i])-sum(num_list)/10)
        if sum_num == 0:
            fit = 11
        else:
            fit = 1 / sum_num # fit 越高,适应度越高

        fit_list.append((fit, i))

    # print(fit_list)

    return fit_list

# fit 是该可行解的适应度,i 是该可行解的索引
# i 的意义是 当对 fit 进行排序时,通过 i 对排序之后适应度相对应的可行解进行排序

基因交换

def change(solve_list):
    """
    PC = 0.7
    :param solve_list: --> 染色体种群
    :return: solve_list: --> 经过基因交换后的染色体种群
    """

    # 选择父代列表和母代列表
    male_list = solve_list[0::2]
    female_list = solve_list[1::2]
    # print(male, female)

    for j in range(len(solve_list)):
        # 选择需要交换基因的两个个体
        # print(j)
        if random.random() < 0.7:
            male = male_list[random.randint(0, len(male_list)-1)]
            female = female_list[random.randint(0, len(female_list)-1)]

            # 两个个体之间的基因交换
            random_index1 = random.randint(0, len(male)-1) # 要交换部分的起始索引点
            random_index2 = random.randint(0, len(male)-1) # 要交换部分的末尾索引点

            while random_index2 <= random_index1:
                random_index1 = random.randint(0, len(male) - 1)
                random_index2 = random.randint(0, len(male) - 1)

            # child1, child2 = male[0:random_index1] + female[random_index1:random_index2] + male[random_index2:], female[0:random_index1] + male[random_index1:random_index2] + female[random_index2:]

            child1_head = []
            child1_tail = []
            for i in male[0:random_index1]:
                num = i
                while num in female[random_index1:random_index2]:
                    num = male[random_index1:random_index2][female[random_index1:random_index2].index(num)]
                child1_head.append(num)
            for i in male[random_index2:]:
                num = i
                while num in female[random_index1:random_index2]:
                    num = male[random_index1:random_index2][female[random_index1:random_index2].index(num)]
                child1_tail.append(num)
            child1 = child1_head + female[random_index1:random_index2] + child1_tail

            child2_head = []
            child2_tail = []
            # 子代2头
            for i in female[0:random_index1]:
                num = i
                while num in male[random_index1:random_index2]:
                    num = female[random_index1:random_index2][male[random_index1:random_index2].index(num)]
                child2_head.append(num)

            # 子代2尾
            for i in female[random_index2:]:
                num = i
                while num in male[random_index1:random_index2]:
                    num = female[random_index1:random_index2][male[random_index1:random_index2].index(num)]
                child2_tail.append(num)
            child2 = child2_head + male[random_index1:random_index2] + child2_tail

            # print(len(set(child1)), len(set(child2)))

            solve_list.append(child1)
            solve_list.append(child2)

    return solve_list

基因变异

def mata(solve_list):
    """
    基因突变
    PM = 0.05 变异概率
    :param solve_list: --> 染色体种群
    :return: solve_list: --> 经过基因交换后的染色体种群
    """
    for i in solve_list:
        if random.random() < PM:
            # print(i)
            change_index  = random.randint(0, len(i)-1)
            rand_num = random.randint(0, num_list_len-1)
            while num_list[rand_num] in i:
                rand_num = random.randint(0, num_list_len - 1)
            i[change_index] = num_list[rand_num]

            solve_list.append(i)

            # print(i)

    return solve_list

归一化处理以及染色体选择复制

def guiyi(init_data, fit_list):
    """
    归一化处理以及染色体选择复制
    :param init_data:
    :param fit_list:
    :return: init_data
    """
    finally_data = []
    
    # 适应度(可行解)排序
    # print(init_data)
    fit_list.sort()
    fit_list = fit_list[::-1]
    # print(fit_list)
    index_list = [i[1] for i  in fit_list]
    init_data = [init_data[i] for i in index_list][0:N]
    fit_list = fit_list[0:N]
    # print(init_data)
    
    # 概率归一化处理
    num_list = [i[0] for i in fit_list]

    P_list = [i / sum(num_list) for i in num_list] # 概率列表
    P_guiyi_list = [P_list[0]] + [P_list[i] for i in range(1, len(P_list))] # 归一之后的概率列表
    
    # 轮盘赌法染色体选择复制
    for i in range(N):
        P_num = random.random()
        for j in range(len(P_guiyi_list)):
            if P_num-j < 0:
                finally_data.append(init_data[j])
                break

    return finally_data

迭代

init_data = init(N) # 初始化可行解
for i in range(T+1):
    fit_list = fitness(init_data) # 计算适应度
    init_data = guiyi(init_data, fit_list) # 轮盘赌法选择 N 规模的 可行解

    # 输出迭代结果
    fit_list = fitness(init_data)
    fit_list.sort()
    res_index_list = (max(fit_list)[1], min(fit_list)[1])
    res_list = [sum(init_data[res_index_list[0]]), sum(init_data[res_index_list[1]])]
    print("第{}次迭代结果:{}---{}".format(i, res_list[0], res_list[1]))

    X.append(i)
    Y.append(res_list[0])

    init_data = change(init_data) # 基因交换
    init_data = mata(init_data) # 基因变异

完整代码

'''
在一个长度为 n 的数组中选择 10 个元素,使得 10个元素的和 与 该元组中元素总和的 1/10 接近
'''

import random
import matplotlib.pyplot as plt

num_list_len = random.randint(10,100)

num_list = []

for i in range(num_list_len):
    num = random.randint(1, 500)
    while num in num_list:
        num = random.randint(1, 500)
    num_list.append(num)

# print(num_list_len, num_list)

# num_list_len = 78
#
# num_list = [35, 30, 52, 26, 18, 70, 56, 44, 74, 71, 38, 40, 31, 90, 98, 80, 78, 50, 16, 12, 14, 19, 76, 82, 79, 11, 33, 55, 37, 88, 73, 99, 72, 65, 29, 96, 66, 97, 54, 62, 100, 85, 61, 41, 20, 49, 13, 68, 51, 28, 86, 21, 34, 83, 63, 47, 64, 48, 46, 67, 10, 95, 58, 93, 59, 39, 94, 36, 87, 42, 84, 43, 22, 15, 17, 77, 24, 32]
#
def init(N):
    """
    初始化染色体
    :param N:
    :return:
    """
    init_data = []

    for j in range(N):
        init_list = []
        for i in range(10):
            rand_num = random.randint(0, num_list_len-1)
            while num_list[rand_num] in init_list:
                rand_num = random.randint(0, num_list_len-1)
            init_list.append(num_list[rand_num])
        init_data.append(init_list)

    return init_data

def fitness(init_data):
    """
    计算适应度
    :param init_data: --> list
    :param N: --> num
    :return:
    """
    fit_list = []
    for i in range(len(init_data)):
        sum_num = abs(sum(init_data[i])-sum(num_list)/10)
        if sum_num == 0:
            fit = 11
        else:
            fit = 1 / sum_num # fit 越高,适应度越高

        fit_list.append((fit, i))

    # print(fit_list)

    return fit_list

def change(solve_list):
    """
    PC = 0.7
    :param solve_list: --> 染色体种群
    :return: solve_list: --> 经过基因交换后的染色体种群
    """

    # 选择父代列表和母代列表
    male_list = solve_list[0::2]
    female_list = solve_list[1::2]
    # print(male, female)

    for j in range(len(solve_list)):
        # 选择需要交换基因的两个个体
        # print(j)
        if random.random() < 0.7:
            male = male_list[random.randint(0, len(male_list)-1)]
            female = female_list[random.randint(0, len(female_list)-1)]

            # 两个个体之间的基因交换
            random_index1 = random.randint(0, len(male)-1) # 要交换部分的起始索引点
            random_index2 = random.randint(0, len(male)-1) # 要交换部分的末尾索引点

            while random_index2 <= random_index1:
                random_index1 = random.randint(0, len(male) - 1)
                random_index2 = random.randint(0, len(male) - 1)

            # child1, child2 = male[0:random_index1] + female[random_index1:random_index2] + male[random_index2:], female[0:random_index1] + male[random_index1:random_index2] + female[random_index2:]

            child1_head = []
            child1_tail = []
            for i in male[0:random_index1]:
                num = i
                while num in female[random_index1:random_index2]:
                    num = male[random_index1:random_index2][female[random_index1:random_index2].index(num)]
                child1_head.append(num)
            for i in male[random_index2:]:
                num = i
                while num in female[random_index1:random_index2]:
                    num = male[random_index1:random_index2][female[random_index1:random_index2].index(num)]
                child1_tail.append(num)
            child1 = child1_head + female[random_index1:random_index2] + child1_tail

            child2_head = []
            child2_tail = []
            # 子代2头
            for i in female[0:random_index1]:
                num = i
                while num in male[random_index1:random_index2]:
                    num = female[random_index1:random_index2][male[random_index1:random_index2].index(num)]
                child2_head.append(num)

            # 子代2尾
            for i in female[random_index2:]:
                num = i
                while num in male[random_index1:random_index2]:
                    num = female[random_index1:random_index2][male[random_index1:random_index2].index(num)]
                child2_tail.append(num)
            child2 = child2_head + male[random_index1:random_index2] + child2_tail

            # print(len(set(child1)), len(set(child2)))

            solve_list.append(child1)
            solve_list.append(child2)

    return solve_list

def mata(solve_list):
    """
    基因突变
    PM = 0.05 变异概率
    :param solve_list: --> 染色体种群
    :return: solve_list: --> 经过基因交换后的染色体种群
    """
    for i in solve_list:
        if random.random() < PM:
            # print(i)
            change_index  = random.randint(0, len(i)-1)
            rand_num = random.randint(0, num_list_len-1)
            while num_list[rand_num] in i:
                rand_num = random.randint(0, num_list_len - 1)
            i[change_index] = num_list[rand_num]

            solve_list.append(i)

            # print(i)

    return solve_list

def guiyi(init_data, fit_list):
    """
    归一化处理以及染色体选择复制
    :param init_data:
    :param fit_list:
    :return: init_data
    """
    finally_data = []

    # 适应度(可行解)排序
    # print(init_data)
    fit_list.sort()
    fit_list = fit_list[::-1]
    # print(fit_list)
    index_list = [i[1] for i  in fit_list]
    init_data = [init_data[i] for i in index_list][0:N]
    fit_list = fit_list[0:N]
    # print(init_data)

    # 概率归一化处理
    num_list = [i[0] for i in fit_list]

    P_list = [i / sum(num_list) for i in num_list] # 概率列表
    P_guiyi_list = [P_list[0]] + [P_list[i] for i in range(1, len(P_list))] # 归一之后的概率列表

    # 轮盘赌法染色体选择复制
    for i in range(N):
        P_num = random.random()
        for j in range(len(P_guiyi_list)):
            if P_num-j < 0:
                finally_data.append(init_data[j])
                break

    return finally_data

N = 50 # 种群规模
T = 200 # 迭代次数
PM = 0.05 # 变异概率
PC = 0.7 # 交换概率

X = []
Y = []

init_data = init(N) # 初始化可行解
for i in range(T+1):
    fit_list = fitness(init_data) # 计算适应度
    init_data = guiyi(init_data, fit_list) # 轮盘赌法选择 N 规模的 可行解

    # 输出迭代结果
    fit_list = fitness(init_data)
    fit_list.sort()
    res_index_list = (max(fit_list)[1], min(fit_list)[1])
    res_list = [sum(init_data[res_index_list[0]]), sum(init_data[res_index_list[1]])]
    print("第{}次迭代结果:{}---{}".format(i, res_list[0], res_list[1]))

    X.append(i)
    Y.append(res_list[0])

    init_data = change(init_data) # 基因交换
    init_data = mata(init_data) # 基因变异

res = sum(num_list) / 10
print(res)

y = [res]* (T+1)
# print(x)

plt.plot(X, Y)
plt.plot(X, y,color='red')
plt.show()

运行结果

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

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

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

相关文章

国产第一颗7A的车规级马达驱动芯片TMI8140-Q1

新能源汽车因其电动化和智能化的特性&#xff0c;需要大量的马达驱动、DC/DC、高效率Buck & BuckBoost等车规级芯片&#xff0c;成为名副其实的“芯片大户”&#xff01; 以往制造一辆传统汽车一般需要用到500-600颗左右的芯片&#xff0c;而一台高性能的新能源汽车需要芯…

上传IPA包到App Store​

我们都经历过上传IPA包到App Store时的痛苦&#xff01;&#xff01;&#xff01;​ 第一步&#xff1a;通过Xcode-> Archive&#xff0c;Validate App&#xff0c;成功&#xff01;​ 第二步&#xff1a;通过Xcode-> Archive&#xff0c;Distribute App&#xff0c;成…

从股票市场选择配对的股票:共同趋势模型与套利定价理论

股价取对数之后的序列建模为随机游走。现在把取对数之后的股价当作是由一个随机游走和一个平稳时序组成&#xff1a; 是随机游走部分&#xff0c;是平稳部分。取对数后的股价求差就得到回报。所以在时间的回报表示为&#xff1a; 表示非平稳部分的回报&#xff0c;表示平稳部分…

如何基于运维事件中心通过 logstash 进行日志关键字监控

日常运维过程中&#xff0c;很多场景都需要对日志关键字进行监测&#xff0c;以便第一时间发现应用/业务相关异常&#xff0c;这是一种比较常见的监控需求&#xff0c;所以也有很多方法可以实现关键字告警。对于简单的告警可以通过一些传统的监控工具实现&#xff0c;但对于体量…

从头开始搭建一个SpringBoot项目--SpringSecurity的配置

从头开始搭建一个SpringBoot项目--SpringSecurity的配置前言本文的目标使用到的依赖、Redis配置、通用返回实体类依赖Redis项目里的配置通用返回实体ResultResultCodeResultUtil配置文件配置的目录结构Spring Security的配置信息SecurityConfigWebMVCConfig用到的类及代码自定义…

深入浅出自定义创建spring-boot-starter

深入浅出自定义创建spring-boot-starter https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-auto-configuration 快速入手 第一步&#xff1a;新建模块 第二步&#xff1a;修改依赖 <?xml version"1.0" e…

关键词(三)

关键词一.最冤枉的关键字—sizeof二.“简单”的布尔类型—_Bool一.最冤枉的关键字—sizeof 前面我们说过定义变量是需要空间的&#xff08;声明不需要&#xff09;&#xff0c;同时你需要有类型像int,char…这些不同的类型会在内存中开辟不同大小的空间&#xff0c;而sizeof就可…

Web安全测试工具AppScan简述

01 安全测试的对象 了解常见的Web应用安全漏洞&#xff0c;参考OWASP Top 10 2017。 理解这些常见漏洞的攻击原理&#xff0c;如何判断系统是否存在这些漏洞、如何防止这些漏洞。 02 安全测试的实施 SQL注入测试 确认所有的解释器都明确的将不可信数据从命令语句或者查询语…

微服务囧途之BFF层登场

从单体架构演化为微服务架构后&#xff0c;架构者的期望是“模块A”“模块B” “后端服务”。 场景一 Web端和Mobile端都有一个详情页面&#xff0c;需要调用模块A的getDetail接口获取数据。假设Web端实际需要展示的字段是20个&#xff0c;Mobile端实际需要展示的字段是10个…

记一次Metrics-server异常

报错 前几天测试环境k8s集群做etcd的备份恢复。 所有的pod都起来了&#xff0c;包括metrics-server的状态也是 Running,部署新pod也没有异常&#xff0c;结果kubectl top 请求的时候报错了 Error from server (ServiceUnavailable): the server is currently unable to handle …

Spring cloud Consul 服务注册中心实战

Spring cloud Consul服务注册中心一、简介二、功能三、角色四、工作原理1、服务注册及发现2、服务调用五、起步1、下载2、安装六、实例一、简介 Consul 用于实现分布式系统的服务注册与配置&#xff0c;与其它方案相比&#xff0c;Consul 更 “一站式”&#xff0c;内置了服务…

移植lighttpd笔记

一、前言 lighttpd交叉编译并移植到iTop4412平台 二、编译环境 ubuntu 18.04pcrelighttpd交叉编译链:arm-none-linux-gnueabi三、编译过程 1.pcre交叉编译 tar -xvf pcre-8.38.tar.bz2 cd pcre-8.38/ ./configure --host=arm-none-linux-gnueabi --prefix=/ --enable-utf8 …

IDEA中如何实现 git stash 命令的可视化操作?

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a;git stash 相关命令 1、git stash 2、git stash save 注释 3、git stash list 4、git stash pop 5、git stash apply stash{编号} 6、git stash drop stash{编号} 7、git stash clear 8、git stash…

flutter 内网安装包生成二维码

参考 前言 有时候我们可能需要用手机访问电脑上的文件, 或者用手机测试电脑上编写的程序 此时 在同一个wifi网络的前提下我们可以这么做: 第一种 使用python 直接使用自带python工具开启http服务, 首先打开终端, 进入需要共享的目录,然后运行以下代码即可: python -m Simple…

React TreeSelect 组件使用和优化

1、自定义折叠和展开图标 此时就要用到switcherIcon属性&#xff0c;以下是antd中对switcherIcon属性的描述&#xff0c; switcherIcon自定义树节点的展开/折叠图标ReactNode | ((props: AntTreeNodeProps) > ReactNode)-具体使用如下&#xff1a; import { DownOutlined…

数据库原理及MySQL应用 | 事件

事件由一个特定的线程——事件调度器来管理&#xff0c;事件是根据指定时间表&#xff0c;在某一特定的时间点&#xff0c;触发相关的SQL语句或存储过程。 01、事件概述 事件(Event)是根据指定时间表执行的任务&#xff0c;是MySQL在相应的时刻调用的过程式数据库对象。它由事…

抓包分析ssh远程主机为何变慢了?

文章目录背景SSH协议握手过程ssh 抓包MAC层包传输tcp握手抓包解释三次握手的第一个报文- SYN包第一个报文对应的抓包详情三次握手的第二个报文- SYNACK包第二个报文对应的抓包详情三次握手的第三个报文- ACK包第三个报文对应的抓包详情ssh版本协议交换密钥协商key阶段Key Excha…

程序员的工资这么高,为什么还会有人离职?

出了社会以后才发现&#xff0c;班级里天天打鸡血的、最奋斗的、同时也最焦虑的&#xff0c;不是成绩最好的&#xff0c;也不是成绩最差的&#xff0c;而是那帮处于中间的人。 他们不像那些成绩最差的&#xff0c;或是天天摆烂&#xff0c;或是靠高情商混得风生水起&#xff1b…

Pytest----pluggy源码解读基础准备

【原文链接】Pytest----pluggy源码解读基础准备 解读pluggy源码&#xff0c;直接使用pytest环境中安装的pluggy即可&#xff0c;比如这里安装的pluggy版本是1.0.0&#xff0c;为了更好的理解源码&#xff0c;这里首先使用如下应用代码作为应用实例&#xff0c;从如下代码中可以…

低代码助力生产管理:离散型制造业MES系统

制造业作为我国国民经济的支柱产业&#xff0c;在我国经济增长中占有主导作用。而制造业对经济增长的贡献很大一部分来自于以离散制造业为代表的机械装备制造、汽车零部件制造等。因此&#xff0c;离散制造业的发展对我国经济增长具有举足轻重的作用。 离散型制造业的特点&…