遗传算法理解与代码实战(二)- demo(python+deap)

前文介绍了遗传算法,并且手动python代码进行了实践,但是在遇到复杂的问题时(遗传算法理解与代码实战(三)会介绍),手写代码很麻烦,所以需要借助专门的遗传算法库来实现,这里我们选择deap库

1、DEAP简介

1.1 核心模块

在DEAP库中,algorithmsbasecreatortools是核心模块,它们提供了构建和运行进化算法所需的各种组件和工具。下面是对这些模块的简要介绍和使用方法:

creator

creator模块用于创建新的类,这些类可以用于表示个体、适应度、策略等。通过使用creator.create函数,可以定义具有特定属性和方法的类。
使用方法:

from deap import creator
# 创建一个表示个体的新类
creator.create("Individual", list, fitness=creator.FitnessMax)
# 创建一个表示适应度的新类
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
# 创建一个具有特定属性和方法的类
creator.create("MyClass", object, attr1=value1, attr2=value2)
base

base模块提供了进化算法的基本工具和组件,如种群、个体、适应度等。它还提供了遗传操作的基本函数,如选择、交叉和变异。
使用方法:

from deap import base
# 创建一个个体类
ind_class = base.Toolbox()
# 注册一个个体创建函数
ind_class.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=5)
# 创建一个种群类
pop_class = base.Toolbox()
# 注册一个种群创建函数
pop_class.register("population", tools.initRepeat, list, ind_class.individual)
tools

tools模块提供了许多有用的工具和函数,用于执行遗传操作、统计信息收集、可视化等。
使用方法:

from deap import tools
# 注册一个评估函数
toolbox.register("evaluate", my_evaluation_function)
# 注册一个选择函数
toolbox.register("select", tools.selTournament, tournsize=3)
# 注册一个交叉函数
toolbox.register("mate", tools.cxTwoPoint)
# 注册一个变异函数
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=1, indpb=0.1)
# 创建一个统计数据对象
stats = tools.Statistics(key=lambda ind: ind.fitness.values)
stats.register("avg", np.mean)
stats.register("min", np.min)
stats.register("max", np.max)
# 创建一个名人堂对象,用于存储最佳个体
hof = tools.HallOfFame(1)
algorithms

algorithms模块提供了各种预定义的进化算法,如简单遗传算法(eaSimple)、遗传编程(eaGP)等。这些算法可以轻松地与toolbox中的组件结合使用。
使用方法:

from deap import algorithms
# 创建一个初始种群
pop = toolbox.population(n=50)
# 运行简单遗传算法
pop, logbook = algorithms.eaSimple(pop, toolbox, cxpb=0.7, mutpb=0.1, ngen=50, stats=stats, halloffame=hof, verbose=True)
# 打印最佳个体
print("Best individual: ", hof[0])

1.2 核心组件

DEAP的核心组件包括:

  • 个体(Individual):表示问题的一个潜在解决方案。
  • 种群(Population):个体的集合,表示一代中的所有解决方案。
  • 适应度(Fitness):评估个体质量的手段,通常是一个或多个数值。
  • 选择(Selection):从种群中选择个体以繁殖后代的过程。
  • 交叉(Crossover):交换两个个体的部分基因以创建新的后代。
  • 变异(Mutation):改变个体基因的过程,以增加种群的多样性。

1.3 主体流程

使用DEAP(Distributed Evolutionary Algorithms in Python)库进行建模的流程通常包括以下几个步骤:

  1. 导入库和模块:
    首先,你需要导入DEAP库以及你将使用的具体模块,如basecreatortoolsalgorithms
  2. 定义问题:
    • 创建个体类和适应度类。使用creator模块创建适应度类(如FitnessMaxFitnessMin),以及个体类(如Individual),这些类将用于表示遗传算法中的个体和它们的适应度。
    • 确定问题的目标是最小化还是最大化,并相应地设置适应度类的权重。
  3. 初始化工具箱:
    • 使用base.Toolbox类创建一个工具箱实例,工具箱将包含所有用于生成和操作个体的工具。
    • 注册基因生成器(如attr_boolattr_float等),个体生成器(如individual),种群生成器(如population)等。
  4. 定义适应度函数:
    • 编写一个函数,用于评估个体的适应度。这个函数将接受一个个体作为输入,并返回一个表示其适应度的值(对于FitnessMax,通常是一个正值;对于FitnessMin,通常是一个负值)。
  5. 注册遗传操作:
    • 使用工具箱注册遗传操作,如选择(select)、交叉(mate)、变异(mutate)等。
    • 你可以选择DEAP提供的预定义操作,也可以自定义自己的操作。
  6. 实现遗传算法主循环:
    • 编写一个主函数(如main),在其中创建初始种群,并执行遗传算法的迭代过程。
    • 在每次迭代中,对种群进行选择、交叉、变异,并评估新生个体的适应度。
    • 更新种群,并记录所需的统计数据或最佳个体。
  7. 分析和可视化结果:
    • 在遗传算法运行结束后,分析结果,可能包括最佳个体的适应度、种群的进化趋势等。
    • 使用DEAP提供的工具或第三方库(如Matplotlib)进行可视化。
  8. 调优和测试:
    • 根据结果调整遗传算法的参数,如种群大小、交叉和变异概率等。
    • 进行多次实验,以验证算法的稳定性和性能。

2、代码实战

目标函数: y = x 2 y=x^2 y=x2
x的取值范围是[0,31]

我们根据前面的流程来尽心操作

2.1 定义问题

# 定义问题:求最大化问题
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", list, fitness=creator.FitnessMax)

2.2 初始化工具箱

# 初始化
toolbox = base.Toolbox()# 定义如何生成一个属性(即基因),这里使用0到1的随机整数
toolbox.register("attr_bool", random.randint, 0, 1)# 定义个体如何生成:创建一个个体,其中包含5个基因
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_bool, n=5)# 定义种群如何生成:种群由个体组成
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
  • attr_bool:
    attr_bool通常用于生成布尔值(True或False)的基因。在遗传算法中,这通常代表个体的一个二进制特征。
    例如,你可以使用attr_bool来生成一个由随机布尔值组成的个体,这些值可以表示遗传算法中的二进制字符串。

  • attr_float:
    attr_float用于生成浮点数的基因。这在处理连续优化问题时非常有用,其中个体的特征需要是实数值。
    你可以指定浮点数的范围,例如,attr_float(0.0, 1.0)将生成一个在0.0到1.0之间均匀分布的随机浮点数。

  • tools.initRepeat是一个函数,用于创建一个个体,其中包含重复的基因。这个函数可以接受以下参数:

    • container:一个用于创建个体的类或类型。通常,这是使用creator模块定义的个体类。
    • func:一个函数,用于生成个体的单个基因。这个函数可以是toolbox.attr_bool、toolbox.attr_float等。
    • n:一个整数,表示个体中基因的数量。
    • *args:额外的位置参数,将传递给func。
    • **kwargs:额外的关键字参数,将传递给func。
toolbox.individual()
[1, 0, 0, 0, 1]toolbox.population(10)
[[1, 0, 0, 1, 1],[1, 0, 1, 0, 0],[0, 1, 1, 1, 0],[1, 1, 0, 1, 1],[1, 0, 1, 0, 1],[0, 0, 0, 1, 0],[1, 1, 0, 1, 1],[0, 1, 1, 1, 1],[0, 0, 0, 0, 0],[0, 1, 1, 1, 0]]

2.3 定义适应度函数

# 定义适应度函数
def evalOneMax(individual):# 将二进制转换为十进制x = int("".join(str(i) for i in individual), 2)# 计算适应度return x**2,
# 将适应度函数与个体评估过程绑定
toolbox.register("evaluate", evalOneMax)

这个与之前一样不过多介绍

2.4 注册遗传操作

# 注册遗传算法操作:交叉、变异、选择
toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
toolbox.register("select", tools.selTournament, tournsize=3)
  • toolbox.register("mate", tools.cxTwoPoint)
    这一行将两点交叉操作(cxTwoPoint)注册到工具箱中,键为"mate"。两点交叉是一种常用的交叉操作,它选择两个交叉点,并交换两个父代个体在这两点之间的基因段来创建两个子代个体。
  • toolbox.register("mutate", tools.mutFlipBit, indpb=0.05)
    这一行将位翻转变异操作(mutFlipBit)注册到工具箱中,键为"mutate"。位翻转变异会随机选择个体的基因位并进行翻转(0变为1,1变为0)。indpb参数是每个基因发生变异的概率,这里是0.05,意味着每个基因有5%的概率发生变异。
  • toolbox.register("select", tools.selTournament, tournsize=3)
    这一行将锦标赛选择操作(selTournament)注册到工具箱中,键为"select"。锦标赛选择是一种选择方法,它从种群中随机选择一定数量的个体(这里由tournsize参数指定为3个),然后选择其中适应度最高的个体作为父代。这种方法有助于增加种群的多样性并防止过早收敛。

2.5 实现遗传算法主循环

def main():random.seed(64)# 创建初始种群pop = toolbox.population(n=50)# CXPB 为交叉概率,MUTPB 为变异概率CXPB, MUTPB, NGEN = 0.5, 0.2, 40# 初始种群的适应度评估fitnesses = list(map(toolbox.evaluate, pop))for ind, fit in zip(pop, fitnesses):ind.fitness.values = fit# 遗传算法循环for g in range(NGEN):# 选择下一代offspring = toolbox.select(pop, len(pop))offspring = list(map(toolbox.clone, offspring))# 交叉和变异for child1, child2 in zip(offspring[::2], offspring[1::2]):if random.random() < CXPB:toolbox.mate(child1, child2)del child1.fitness.valuesdel child2.fitness.valuesfor mutant in offspring:if random.random() < MUTPB:toolbox.mutate(mutant)del mutant.fitness.values# 重新评估变异和交叉后个体的适应度invalid_ind = [ind for ind in offspring if not ind.fitness.valid]fitnesses = map(toolbox.evaluate, invalid_ind)for ind, fit in zip(invalid_ind, fitnesses):ind.fitness.values = fit# 新的种群pop[:] = offspring# 输出当前代的最佳个体fits = [ind.fitness.values[0] for ind in pop]length = len(pop)mean = sum(fits) / lengthmax_fit = max(fits)print(f"代 {g}, 最大适应度 {max_fit}")best_ind = tools.selBest(pop, 1)[0]print("最佳个体是 %s, %s" % (best_ind, best_ind.fitness.values))
# 初始种群的适应度评估
fitnesses = list(map(toolbox.evaluate, pop))
for ind, fit in zip(pop, fitnesses):ind.fitness.values = fit

初始化一个种群并计算这个种群中每个个体的适应度,为遗传算法的后续迭代过程做准备。
遍历完成后我们可以打印下种群的数据

for ind in pop:print(ind, ind.fitness.values)[0, 0, 1, 0, 1] (25.0,)
[0, 1, 0, 1, 1] (121.0,)
[0, 0, 0, 1, 1] (9.0,)
[1, 0, 1, 0, 1] (441.0,)
[1, 0, 1, 1, 0] (484.0,)
[1, 0, 1, 0, 1] (441.0,)
[1, 1, 1, 0, 0] (784.0,)
……
  • toolbox.select(pop, len(pop)) 这行代码的作用是从当前种群(pop)中选择出新的后代种群,选择的数量与当前种群数量(len(pop))相同。选择过程通常基于个体的适应度,适应度高的个体有更大的概率被选中。
  • offspring = list(map(toolbox.clone, offspring)) 这行代码的作用是对选中的后代个体进行克隆。在DEAP库中,个体(Individual)是通过列表(list)实现的,而在遗传算法中,为了避免在操作过程中修改原始种群,通常会对选中的个体进行克隆。map(toolbox.clone, offspring) 对 offspring 中的每个个体执行 toolbox.clone 函数,返回一个克隆个体的迭代器。list() 函数将这个迭代器转换为列表,从而得到一个包含克隆个体的新种群。
# 交叉和变异
for child1, child2 in zip(offspring[::2], offspring[1::2]):if random.random() < CXPB:toolbox.mate(child1, child2)del child1.fitness.valuesdel child2.fitness.values
  • zip(offspring[::2], offspring[1::2]) 创建了一个迭代器,它将 offspring 列表中的个体两两配对。offspring[::2] 表示选择 offspring 列表中的所有偶数索引个体,而 offspring[1::2] 表示选择所有奇数索引个体。这样,每次迭代都会得到一对个体(child1 和 child2)。
  • if random.random() < CXPB: 检查一个随机数是否小于交叉概率(CXPB)。如果条件为真,则执行交叉操作;否则,不执行交叉,这对个体将保持不变。
  • toolbox.mate(child1, child2) 调用注册到工具箱中的交叉操作函数(在这个例子中是 tools.cxTwoPoint,两点交叉),对 child1 和 child2 进行交叉,生成新的后代。这个函数会修改 child1 和 child2 的基因(即它们的属性)。
  • del child1.fitness.values 和 del child2.fitness.values 删除 child1 和 child2 的适应度值。这是因为交叉操作改变了个体的基因,因此它们之前的适应度值不再有效。在后续的代码中,需要重新评估这些个体的适应度。
for mutant in offspring:if random.random() < MUTPB:toolbox.mutate(mutant)del mutant.fitness.values
  • for mutant in offspring: 这个循环遍历所有后代个体(offspring 列表中的每个个体)。
  • if random.random() < MUTPB: 检查一个随机数是否小于变异概率(MUTPB)。如果条件为真,则对当前遍历到的个体(mutant)执行变异操作;否则,不执行变异,该个体将保持不变。
  • toolbox.mutate(mutant) 调用注册到工具箱中的变异操作函数,对 mutant 进行变异。在这个例子中,变异操作函数是 tools.mutFlipBit,它随机翻转个体基因中的一个位(bit)。
  • del mutant.fitness.values 删除 mutant 的适应度值。这是因为变异操作改变了个体的基因,因此它们之前的适应度值不再有效。在后续的代码中,需要重新评估这些个体的适应度。

在这里插入图片描述

# 重新评估变异和交叉后个体的适应度
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):ind.fitness.values = fit
  • invalid_ind = [ind for ind in offspring if not ind.fitness.valid] 这行代码使用列表推导式创建一个新列表,包含所有适应度无效的个体。在DEAP库中,个体的适应度可以通过 ind.fitness.valid 属性来检查是否有效。如果个体经历了交叉或变异,其适应度通常会标记为无效。
  • fitnesses = map(toolbox.evaluate, invalid_ind) 使用 map 函数和工具箱中的 evaluate 函数来计算 invalid_ind 列表中所有个体的适应度。toolbox.evaluate 是之前注册的适应度函数,用于评估个体的适应度。
  • for ind, fit in zip(invalid_ind, fitnesses): 这个循环遍历所有无效个体和它们的适应度值。
    • ind.fitness.values = fit 将计算出的适应度值赋给对应的个体。这样,每个经过交叉或变异的个体的适应度就被更新为最新的值。
pop[:] = offspring# 输出当前代的最佳个体fits = [ind.fitness.values[0] for ind in pop]length = len(pop)mean = sum(fits) / lengthmax_fit = max(fits)print(f"代 {g}, 最大适应度 {max_fit}")

这段代码是将新的后代种群(offspring)替换当前的种群(pop),并输出当前种群中最佳个体的适应度。下面是这段代码的详细解释:

  1. pop[:] = offspring 这行代码将新的后代种群(offspring)替换当前的种群(pop)。在Python中,pop[:] 表示对整个种群列表的引用,而 offspring 是通过选择、交叉和变异操作产生的新种群。这行代码实际上是更新了 pop 列表的内容,使其包含新一代的个体。
  2. fits = [ind.fitness.values[0] for ind in pop] 这行代码使用列表推导式来创建一个新列表,包含当前种群(pop)中每个个体的适应度值。在DEAP库中,个体的适应度值通常是一个元组,即使对于单目标优化问题也是如此。因此,ind.fitness.values[0] 用于获取适应度元组中的第一个元素,即实际的适应度值。
  3. length = len(pop) 获取当前种群的大小(个体数量)。
  4. mean = sum(fits) / length 计算当前种群的平均适应度。sum(fits) 计算所有个体适应度的总和,然后除以种群大小(length)得到平均值。
  5. max_fit = max(fits) 找出当前种群中的最佳适应度值。
  6. print(f"代 {g}, 最大适应度 {max_fit}") 这行代码打印出当前代数(g)和种群中的最大适应度值(max_fit)。这是为了监控算法的进度和性能。
0, 最大适应度 961.01, 最大适应度 961.02, 最大适应度 961.03, 最大适应度 961.04, 最大适应度 961.05, 最大适应度 961.06, 最大适应度 961.07, 最大适应度 961.08, 最大适应度 961.09, 最大适应度 961.0
最佳个体是 [1, 1, 1, 1, 1], (961.0,)

本篇是遗传算法系列的第二篇,计划总共写三篇,最后会写利用遗传算法来优化Kmeans算法的介绍

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

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

相关文章

使用IAD电话交换机(语音网关)将电话外线对接到FreeSWITCH SIP服务器

在我们初步了解了FreeSWITCH这样的SIP服务器之后&#xff0c;常见的一个需求就是把真实的电信世界&#xff08;比如固话、手机&#xff09;对接到SIP服务器里。 今天我们就介绍一个简单的方法&#xff0c;在3分钟内就把电信局和你的SIP软交换机融合通信起来。 IAD和SIP服务器环…

Superset二次开发之Superset架构理解

技术框架 框架作用 前端 React构建用户界面的 JavaScript 库ReduxJavaScript 应用的状态管理库,管理组件间的状态共享Webpack前端资源打包工具BabelJavaScript 编译器D3.js数据可视化的 JavaScript 库后端 Flask轻量级的 Python web 框架,处理 HTTP 请求、路由以及其他 Web 服…

搜维尔科技:动作捕捉与数字时尚:Wondar Studios欧莱雅项目

来自意大利的Wondar Studios工作室&#xff0c;是一家制作与动作捕捉技术相关软件和内容的公司&#xff0c;其出品的三维角色动画均由专业动捕系统真实录制制作。 我们很高兴与大家分享Wondar Studios最新的动捕项目&#xff0c;该项目带来了身临其境的虚拟现实体验。他们与巴…

AI新工具 MacOS 翻译提供翻译、润色和语法修改功能的插件;AI生成 Excel公式;Deepmind前华人员工创建视频生成工具

1: OpenAI Translator Bob Plugin macOS 平台的翻译提供翻译、润色和语法修改功能的插件 OpenAI Translator Bob Plugin是一款基于OpenAI的API&#xff0c;为用户提供翻译、润色和语法修改功能的插件。这款插件专门为macOS平台上的Bob软件设计&#xff0c;通过使用先进的Chat…

fork属性的作用

在Maven中&#xff0c;fork属性是maven-compiler-plugin插件的一个配置选项。它的作用是控制是否为编译过程启动一个新的进程。当fork设置为true时&#xff0c;Maven将启动一个新的进程来执行编译&#xff0c;这允许你在本地的Java编译器之外使用其他Java编译器&#xff0c;例如…

矩阵键盘中为什么有键位并联二极管?

矩阵键盘上使用二极管主要是为了解决“幽灵键”&#xff08;Ghosting&#xff09;和“键盘冲突”&#xff08;Key Jamming&#xff09;问题&#xff0c;这两种现象在没有采取适当措施的矩阵键盘中很常见。 幽灵键现象 当用户在矩阵键盘上同时按下多个键时&#xff0c;由于电流…

笔试刷题记录(二)

数值操作类型转换 class CompareReference{public static void main(String [] args){float f42.0f;float f1[]new float[2];float f2[]new float[2];float[] f3f1;long x42;f1[0]42.0f;} }A f1f2 B xf1[0] C f1f3 D f2f1[1]BC正确&#xff0c;选项B解释&#xff0c;java核…

如何定义resultType和resultMap,它们之间的区别是什么?解释一下<parameterType>的作用和用法。

在MyBatis中&#xff0c;resultType和resultMap都用于将数据库查询结果映射到Java对象&#xff0c;但它们在使用方式和灵活性上有一些区别。 resultType resultType是一个简单的类型别名&#xff0c;它用于指定查询结果应该映射到的Java类型。当数据库表中的列名和Java对象的属…

设计模式-建造者模式实践案例

建造者模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的最佳方式。当一个对象需要多个部分或许多步骤来创建&#xff0c;并且需要将创建过程与表示分离时&#xff0c;建造者模式非常有用。建造者模式旨在找到一个解决方案&…

C#使用泛型自定义的方法设计队列CQueue<T>类

目录 一、涉及到的知识点 1.C#中的队列类 2.自定义队列的方法 &#xff08;1&#xff09;先设计一个CList<T>类 &#xff08;2&#xff09;再设计CQueue<T>类 二、自定义队列CQueue<T>类的实例 一、涉及到的知识点 1.C#中的队列类 在C#中实现队列类&a…

数据仓库实战 第一讲

第一节 数据仓库的定义 数据仓库的优势 面向主题集成相对稳定反映历史变化 数据仓库的架构 特点 简单通过暂存区简化数据准备星型结构沙盒 &#xff08;提供专用安全区域&#xff09; 云数据仓库 特点 弹性&#xff08;高扩展性能&#xff09;简单易用易于管理节约成本…

线性代数的学习路线

线性代数是数学的一个重要分支&#xff0c;它主要研究向量空间和线性映射。学习线性代数的线索可以从以下几个关键点展开&#xff1a; 向量的内积&#xff1a;了解向量的内积概念&#xff0c;它是衡量两个向量之间关系的一种方式&#xff0c;可以用来计算向量的长度和角度。矩…

Git误操作补救错失:恢复误删的本地分支、将某个提交从一个分支复制到另一个分支

一、恢复误删的本地分支 作为一枚强迫症&#xff0c;没用的分支总是喜欢及时删删删删掉删掉统统删掉&#xff0c;结果今天发现有些分支还是应该保留。 比如&#xff0c;①前段时间切了个分支用来专门做图表&#xff0c;但因为需求还没有最终确定&#xff0c;已经上线了测试服而…

数据库-第八/九章 数据库编程和优化【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下数据库系统概论中的重点概念&#xff0c;以供大家期末复习和考研复习的时候使用。 参考资料是王珊老师和萨师煊老师的数据库系统概论(第五版)。 文章目录 前言第八章 数据库编程8.1 嵌入式SQL8.2 嵌入式sql语…

Java实战:电商平台通用取货码设计

在电商平台中&#xff0c;取货码是一种常见的机制&#xff0c;用于验证用户身份并允许他们从仓库或商店取货。一个好的取货码设计可以提高用户体验&#xff0c;增强安全性&#xff0c;并简化物流流程。本文将深入探讨电商平台通用取货码的设计原理&#xff0c;包括安全性、唯一…

UE4 Niagara 关卡4.1官方案例解析

we now directly supporting playing audio from arbitrary locations in particle systems.users have control over volume and pitch,and the system can directly play sound waves,or sound cues which have multiple waves in them.(我们现在直接支持在粒子系统中从任意…

Python 过滤函数filter()详解

一、过滤函数定义 它用于对容器中的元素进行过滤处理。 二、 过滤函数语法 filter(function,iterable) 参数function&#xff1a;提供过滤条件的函数&#xff0c;返回布尔型 参数iterable: 容器类型数据 三、过滤函数的应用场景 1、筛选符合条件的元素 需求&#xff1a;在列表…

Paddle上手实战——NLP经典cls任务“推特文本情感13分类”

Paddle上手实战——NLP经典cls任务“推特文本情感13分类” 实战背景介绍 数据地址:https://www.heywhale.com/home/activity/detail/611cbe90ba12a0001753d1e9/content Twitter推文具备多重特性,首要之处在于其与Facebook的显著区别——其完全基于文本形式,通过Twitter接…

微软大中华区商业应用事业部高级产品经理张诗源,将出席“ISIG-低代码/零代码技术与应用发展峰会”

3月16日&#xff0c;第四届「ISIG中国产业智能大会」将在上海中庚聚龙酒店拉开序幕。本届大会由苏州市金融科技协会指导&#xff0c;企智未来科技&#xff08;LowCode低码时代、RPA中国、AIGC开放社区&#xff09;主办。大会旨在聚合每一位产业成员的力量&#xff0c;深入探索低…

shell中判断字符串包含另一个字符串

除了用外部的命令比如expr、awk、sed等等&#xff0c;内置方法可以用截取、替换等方式得到一个新的字符串&#xff0c;如果新的字符串和原来的字符串有变化&#xff0c;说明变量包含字符串。 用替换为例: 查找变量a&#xff08;值复制为hello world ) 中是否有字符串or截取、替…