遗传算法求解旅行商问题(含python源代码)

目录

前言

编码初始化种群

计算适应度

选择

交叉

变异

完整代码

总结


前言

这次的算法有一点不能确定是否正确,希望有大佬能够批评指正。

遗传算法的一般步骤

编码初始化种群

种群(population)指同一时间生活在一定自然区域内,同种生物的所有个体。

所以种群是由个体组成的,所以先需要得到个体,然后再随机产生一定数目的个体。

在本算法中个体采用的是实数编码

先对城市的位置进行初始化,采用的是用列表来表示城市的坐标,可以单个定义,也可以随机生成。

先生成一串有序的数字用来表示城市的编号,再每次随机进行打乱后储存到pop列表中,pop表示种群,里面装着的是列表用来表示个体。(个体表示城市的编号,种群表示编号打乱后个体的集合)

群体规模太小,不能提供足够的采样点,以致算法性能很差,易陷入局部最优解。

群体规模太大,尽管可以增加优化信息,阻止早熟收敛的发生,但无疑会增加计算量,造成收敛时间太长,表现为收敛速度缓慢。

City_Map = 100 * np.random.rand(20, 2)  # 随机产生20个城市(20行2列,数值乘以100)
DNA_SIZE = len(City_Map)  # 编码长度(返回行的个数)
POP_SIZE = 100  # 种群大小
# 生成初代种群pop
pop = []
list = list(range(DNA_SIZE))  # 生成[0,DNA_SIZE)的列表
for i in range(POP_SIZE):  # POP_SIZE是指种群大小,在程序中是一个固定的值(打乱POP_SIZE次之后把结果储存到pop列表中random.shuffle(list)  # 随机打乱list,进行初始化操作l = list.copy()  # 把list中的数据拷贝到l中pop.append(l)  # 将l添加到pop列表中

计算适应度

适应度函数值只能是正值,越大越好。

DNA表示个体,根据个体的值(表示城市的编号)来计算距离。

旅行商问题要求距离越短越好,所以距离越大越不满足要求,故而可以通过对距离求倒数来表示适应度。

在最后减去适应度最小的值,可以保证适应度都为正值。(如果有负数,减去一个更小的负数,会变成正值)

def distance(DNA):  # 根据DNA的路线计算距离dis = 0temp = City_Map[DNA[0]]for i in DNA[1:]:# sqrt(pow(x-x0,2)+pow(y-y0,2))dis = dis + ((City_Map[i][0] - temp[0]) ** 2 + (City_Map[i][1] - temp[1]) ** 2) ** 0.5temp = City_Map[i]return dis + ((temp[0] - City_Map[DNA[0]][0]) ** 2 + (temp[1] - City_Map[DNA[0]][1]) ** 2) ** 0.5def getfitness(pop):  # 计算种群适应度,这里适应度用距离的倒数表示temp = []for i in range(len(pop)):temp.append(1 / (distance(pop[i])))# 减去最小值是为了防止适应度出现负值return temp - np.min(temp)

选择

选择操作也称为复制( reproduction) 操作:从当前群体中按照一定概率选出优良的个体, 使它们有机会作为父代繁殖下一代子孙。
判断个体优良与否的准则是各个个体的适应度值:个体适应度越高, 其被选择的机会就越多。

在程序中个体的选择方法采用的是轮盘赌的方法:

按个体的选择概率产生一个轮盘,轮盘每个区的角度与个体的选择概率成比例。
产生一个随机数, 它落入转盘的哪个区域就选择相应的个体交叉
适应度的小的个体也有可能被选中。

def select(pop, fitness):  # 根据适应度选择,以赌轮盘的形式,适应度越大的个体被选中的概率越大# print(fitness)s = fitness.sum()# np.random.choice(a,size,replace,p=None)随机抽取样本a,表示范围,replace=True被抽中后仍有机会被再次抽中,p没抽中的概率temp = np.random.choice(np.arange(len(pop)), size=POP_SIZE, replace=True, p=(fitness / s))p = []for i in temp:p.append(pop[i])return p

交叉

程序中交叉采用部分匹配交叉,如果直接采用两点交叉会导致一个个体中出现两个重复的城市。

部分匹配交叉保证了每个染色体中的基因仅出现一次,通过该交叉策略在一个染色体中不会出现重复的基因,所以部分匹配交叉经常用于旅行商(TSP)或其他排序问题编码。部分匹配交叉类似于两点交叉,通过随机选择两个交叉点确定交叉区域。执行交叉后一般会得到两个无效的染色体,个别基因会出现重复的情况,为了修复染色体,可以在交叉区域内建立每个染色体的匹配关系,然后在交叉区域外对重复基因应用此匹配关系就可以消除冲突。

交叉概率太大时,种群中个体更新很快,会造成高适应度值的个体很快被破坏掉;

概率太小时,交叉操作很少进行,从而会使搜索停滞不前,造成算法的不收敛。

def crossmuta(pop, CROSS_RATE):  # 交叉变异new_pop = []for i in range(len(pop)):  # 遍历种群中的每一个个体,将该个体作为父代n = np.random.rand()if n >= CROSS_RATE:  # 大于交叉概率时不发生变异,该子代直接进入下一代temp = pop[i].copy()new_pop.append(temp)  # 直接进行拷贝if n < CROSS_RATE:  # 小于交叉概率时发生变异list1 = pop[i].copy()list2 = pop[np.random.randint(POP_SIZE)].copy()  # 选取种群中另一个个体进行交叉(随机选择)status = Truewhile status:  # 产生2个不相等的节点,中间部分作为交叉段,采用部分匹配交叉(直到k1<k2的时候才会跳出循环)k1 = random.randint(0, len(list1) - 1)k2 = random.randint(0, len(list2) - 1)if k1 < k2:status = Falsek11 = k1  # 保存切片起始的下标# 先对部分片段进行切片,把切片出来的内容进行交换(完全交换)fragment1 = list1[k1: k2]fragment2 = list2[k1: k2]list1[k1: k2] = fragment2list2[k1: k2] = fragment1del list1[k1: k2]  # 删除list1中[k1,k2)的内容left1 = list1# 进行部分匹配的交叉offspring1 = []#后代#对left1中的每一个位置pos遍历for pos in left1:#检查它是否存在于frag2中if pos in fragment2:#从fragment1中找到对应的基因pos = fragment1[fragment2.index(pos)]#直到基因不再fragment2中为止(遍历fragment2,确保每一个基因都和pos不同)while pos in fragment2:pos = fragment1[fragment2.index(pos)]offspring1.append(pos)continue#如何pos不存在fragment2中,那么就直接将其添加到新的后代中offspring1.append(pos)# 插入新片段for i in range(0, len(fragment2)):offspring1.insert(k11, fragment2[i])k11 += 1temp = offspring1.copy()mutation(temp, MUTA_RATE)  # 进行变异new_pop.append(temp)  # 把部分匹配交叉后形成的合法个体加入到下一代种群return new_pop

变异

互换变异:随机选取染色体的两个基因进行简单互换。
采用随机的形式,在[0,DNA_SIZE)的范围内生成两个下标(确保两个位置不一致),在将两个位置上面的值进行交换。

变异概率太小则很难产生新模式,变异概率太大则会使遗传算法成为随机搜索算法。

def mutation(DNA, MUTA_RATE):  # 进行变异# 两点变异if np.random.rand() < MUTA_RATE:  # 以MUTA_RATE的概率进行变异mutate_point1 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置mutate_point2 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置while (mutate_point1 == mutate_point2):  # 保证2个所选位置不相等mutate_point2 = np.random.randint(0, DNA_SIZE)  #如果相等将mutate_point2重新进行随机生成位置DNA[mutate_point1], DNA[mutate_point2] = DNA[mutate_point2], DNA[mutate_point1]  # 2个所选位置进行互换

逆转变异:在个体码串中随机选择两点( 逆转点) ,然后将两点之间的基因值以逆向排序插入到原位置中。

随机生成两个下标,如x1,x2(确保x1<x2),对列表进行[x1,x2)的切片,在原始的列表中删除切片的部分,将切片的部分翻转之后添加到原始的位置。

def mutation(DNA, MUTA_RATE):  # 进行变异# 逆转变异if np.random.rand() < MUTA_RATE:  # 以MUTA_RATE的概率进行变异status = Truewhile status:  # 产生2个不相等的节点,中间部分作为交叉段,采用部分匹配交叉(直到mutate_point1<mutate_point2的时候才会跳出循环)mutate_point1 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因片段的起始的位置mutate_point2 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因片段的结束的位置if mutate_point1 < mutate_point2:status = Falsek1 = mutate_point1  # 保存切片起始的下标temp = DNA[mutate_point1:mutate_point2]  # 把需要逆转的片段先提取出来del DNA[mutate_point1:mutate_point2]  # 先暂时删除这段片段temp.reverse()  # 反转基因序列# 插入翻转后的新片段for i in range(0, len(temp)):DNA.insert(k1, temp[i])k1 += 1

插入变异:在个体码串中随机选择一个码, 然后将此码插入随机选择的插入点中间。

随机生成两个实数,这次不是交换,是插入的方式,也就是插入点之后的元素的位置都会发生改变。

def mutation(DNA, MUTA_RATE):  # 进行变异#插入变异if np.random.rand() < MUTA_RATE:  # 以MUTA_RATE的概率进行变异mutate_point1 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置(选中一个基因)mutate_point2 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置(插入点)while (mutate_point1 == mutate_point2):  # 保证2个所选位置不相等mutate_point2 = np.random.randint(0, DNA_SIZE)temp=DNA[mutate_point1]#先保存mutate_point1对应的值del DNA[mutate_point1]#删除mutate_point1对应的值DNA.insert(mutate_point2,temp)#重新插入到列表中

完整代码

import time
import numpy as np
import random
import matplotlib.pyplot as plt# 各个城市的坐标
City_Map = 100 * np.random.rand(10, 2)  # 随机产生20个城市(20行2列,数值乘以100)DNA_SIZE = len(City_Map)  # 编码长度(返回行的个数)
POP_SIZE = 100  # 种群大小
CROSS_RATE = 0.85  # 交叉率
MUTA_RATE = 0.15  # 变异率
Iterations = 500  # 迭代次数def distance(DNA):  # 根据DNA的路线计算距离dis = 0temp = City_Map[DNA[0]]for i in DNA[1:]:# sqrt(pow(x-x0,2)+pow(y-y0,2))dis = dis + ((City_Map[i][0] - temp[0]) ** 2 + (City_Map[i][1] - temp[1]) ** 2) ** 0.5temp = City_Map[i]return dis + ((temp[0] - City_Map[DNA[0]][0]) ** 2 + (temp[1] - City_Map[DNA[0]][1]) ** 2) ** 0.5def getfitness(pop):  # 计算种群适应度,这里适应度用距离的倒数表示temp = []for i in range(len(pop)):temp.append(1 / (distance(pop[i])))# 减去最小值是为了防止适应度出现负值return temp - np.min(temp)def select(pop, fitness):  # 根据适应度选择,以赌轮盘的形式,适应度越大的个体被选中的概率越大# print(fitness)s = fitness.sum()# np.random.choice(a,size,replace,p=None)随机抽取样本a,表示范围,replace=True被抽中后仍有机会被再次抽中,p没抽中的概率temp = np.random.choice(np.arange(len(pop)), size=POP_SIZE, replace=True, p=(fitness / s))p = []for i in temp:p.append(pop[i])return pdef mutation(DNA, MUTA_RATE):  # 进行变异# 两点变异if np.random.rand() < MUTA_RATE:  # 以MUTA_RATE的概率进行变异mutate_point1 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置mutate_point2 = np.random.randint(0, DNA_SIZE)  # 随机产生一个实数,代表要变异基因的位置while (mutate_point1 == mutate_point2):  # 保证2个所选位置不相等mutate_point2 = np.random.randint(0, DNA_SIZE)  #如果相等将mutate_point2重新进行随机生成位置DNA[mutate_point1], DNA[mutate_point2] = DNA[mutate_point2], DNA[mutate_point1]  # 2个所选位置进行互换def crossmuta(pop, CROSS_RATE):  # 交叉变异new_pop = []for i in range(len(pop)):  # 遍历种群中的每一个个体,将该个体作为父代n = np.random.rand()if n >= CROSS_RATE:  # 大于交叉概率时不发生变异,该子代直接进入下一代temp = pop[i].copy()new_pop.append(temp)  # 直接进行拷贝if n < CROSS_RATE:  # 小于交叉概率时发生变异list1 = pop[i].copy()list2 = pop[np.random.randint(POP_SIZE)].copy()  # 选取种群中另一个个体进行交叉(随机选择)status = Truewhile status:  # 产生2个不相等的节点,中间部分作为交叉段,采用部分匹配交叉(直到k1<k2的时候才会跳出循环)k1 = random.randint(0, len(list1) - 1)k2 = random.randint(0, len(list2) - 1)if k1 < k2:status = Falsek11 = k1  # 保存切片起始的下标# 先对部分片段进行切片,把切片出来的内容进行交换(完全交换)fragment1 = list1[k1: k2]fragment2 = list2[k1: k2]list1[k1: k2] = fragment2list2[k1: k2] = fragment1del list1[k1: k2]  # 删除list1中[k1,k2)的内容left1 = list1# 进行部分匹配的交叉offspring1 = []#后代#对left1中的每一个位置pos遍历for pos in left1:#检查它是否存在于frag2中if pos in fragment2:#从fragment1中找到对应的基因pos = fragment1[fragment2.index(pos)]#直到基因不再fragment2中为止(遍历fragment2,确保每一个基因都和pos不同)while pos in fragment2:pos = fragment1[fragment2.index(pos)]offspring1.append(pos)continue#如何pos不存在fragment2中,那么就直接将其添加到新的后代中offspring1.append(pos)# 插入新片段for i in range(0, len(fragment2)):offspring1.insert(k11, fragment2[i])k11 += 1temp = offspring1.copy()mutation(temp, MUTA_RATE)  # 进行变异new_pop.append(temp)  # 把部分匹配交叉后形成的合法个体加入到下一代种群return new_popdef print_info(pop):  # 用于输出结果fitness = getfitness(pop)maxfitness = np.argmax(fitness)  # 得到种群中最大适应度个体的索引# 打印结果print("最优的基因型:", pop[maxfitness])print("最短距离:", distance(pop[maxfitness]))# 按最优结果顺序把地图上的点加入到best_map列表中best_map = []for i in pop[maxfitness]:best_map.append(City_Map[i])best_map.append(City_Map[pop[maxfitness][0]])X = np.array((best_map))[:, 0]Y = np.array((best_map))[:, 1]# 绘制地图以及路线plt.figure()plt.rcParams['font.sans-serif'] = ['SimHei']plt.scatter(X, Y)for dot in range(len(X) - 1):plt.annotate(pop[maxfitness][dot], xy=(X[dot], Y[dot]), xytext=(X[dot], Y[dot]))plt.annotate('start', xy=(X[0], Y[0]), xytext=(X[0] + 1, Y[0]))plt.plot(X, Y)if __name__ == "__main__":  # 主循环# 生成初代种群poppop = []list = list(range(DNA_SIZE))  # 生成[0,DNA_SIZE)的列表for i in range(POP_SIZE):  # POP_SIZE是指种群大小,在程序中是一个固定的值(打乱POP_SIZE次之后把结果储存到pop列表中random.shuffle(list)  # 随机打乱list,进行初始化操作l = list.copy()  # 把list中的数据拷贝到l中pop.append(l)  # 将l添加到pop列表中best_dis = []# 最好适应度#goodFitness = 0# 最差适应度(如果进行归一化处理之后,适应度都减去最小值,那么他的最差适应度不都就是0了)#chaFitness = 0# 总体适应度#sumFitness = 0# 所有数量#sumCount = 0# 平均适应度#averageFitness = 0# 获取当前时间(算法开始时间)start_time = time.time()# 进行选择,交叉,变异,并把每代的最优个体保存在best_dis中for i in range(Iterations):  # 迭代N代pop = crossmuta(pop, CROSS_RATE)  # CROSS_RATE交叉率fitness = getfitness(pop)  # 得到适应度种群的适应度# 更新最差适应度# print(np.min(fitness))tmpfitness = np.max(fitness)# 更新最好适应度#if tmpfitness > goodFitness:#    goodFitness = tmpfitness# print(goodFitness)# 记录所有适应度的值#sumFitness = np.sum(fitness) + sumFitness# 记录所有适应度的个数#sumCount = sumCount + np.size(fitness)maxfitness = np.argmax(fitness)  # 返回数值最大的索引best_dis.append(distance(pop[maxfitness]))pop = select(pop, fitness)  # 选择生成新的种群(适应度最大的)print("iteration", i)#averageFitness = sumFitness / sumCount# 获取当前时间(算法结束时间)end_time = time.time()print_info(pop)  # 打印信息print('逐代的最小距离:', best_dis)#print(f'最好适应度:{goodFitness:.4f}')#print(f'最差适应度:{chaFitness:.4f}')#print(f'平均适应度:{averageFitness:.4f}')print(f'程序运行时间:{(end_time - start_time):.4f}秒')#print(pop)# 画图
plt.figure()
plt.plot(range(Iterations), best_dis)
plt.show()
plt.close()

参考信息

遗传算法入门详解 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/100337680遗传算法python进阶理解+论文复现(纯干货,附前人总结引路)_python神经网络遗传算法_不想秃头的夜猫子的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/golden_knife/article/details/128510731通俗易懂地解释遗传算法 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/136393730遗传算法解决旅行商问题(详细解释+代码分享) - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/344588977用遗传算法求解旅行商问题_中国旅行商问题,34个省会-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/breeze_blows/article/details/102992997遗传算法(三)——适应度与选择_遗传算法适应度函数-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_30239361/article/details/101540896

总结

这个最好适应度,最差适应度以及平均适应度的概念没完全掌握,不知道是不是这一个意思,所以在程序中注释了,大家可以根据自己的理解来添加。

遗传算法的思路是“适者生存,优胜劣汰”,模拟生物的进化,以一个初始生物群体为起点,经过竞争后,一部分个体被淘汰而无法再进入这个循环圈,而另一部分则胜出成为种群。对于算法的选择的个体,适应度高的并不一定进入种群,只是进入种群的可能性比较大;而适应度低的个体并不一定被淘汰,只是进入种群的可能性比较小,这个也是与生物进化论相挂钩的。

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

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

相关文章

C++ 继承

前言 本文将会为你带来关于继承的相关知识&#xff08;概念、定义、基类的成员变量访问形式、隐藏、父子类之间的赋值、派生类的默认成员函数、菱形继承与虚继承&#xff09; 继承的概念以及定义 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&…

ANR系列之八:疑难ANR问题处理记录

前言&#xff1a; 本文仅是记录作者自身处理过的ANR问题&#xff0c;以及帮助他人解决过的ANR问题。本文中所介绍的ANR处理记录仅供参考&#xff0c;并不适用所有场景。并且最终结论和分析并不一定就是绝对正确的。 案例1.页面切换时前台应用焦点未获得 案例编号&#xff1a;…

无代码的未来

随着无代码技术越来越成熟&#xff0c;很多web应用已经可以基于无代码平台进行开发。本文分析了4个最流行的无代码平台&#xff0c;并梳理了无代码行业今后可能的发展方向。原文: The future of NoCode 所有无代码编辑器都需要回答的问题 当需要选择无代码解决方案时&#xff0…

Vue基础语法核心指令过滤器计算属性监听属性

目录 1. 模板语法 1.1 插值 1.1.1 文本 1.1.2 html 1.1.3 属性 1.1.4 表达式 1.2 指令 1.2.1 核心指令 1.2.1.1 v-if |v-else-if|v-else 1.2.1.2 v-show 1.2.1.3 v-for 1.2.1.4 v-on|v-model|v-for 1.2.1.5 参数 v-bind:href,v-on:click 1.2.1.6 简写 2. 过滤器…

[AUTOSAR][网络管理] 什么是BusOff? 如何实现它?

文章目录 一、BusOff检测机制(1)简介(2)目录介绍(3)软件实现逻辑代码运行流程如下:二、测试方法(1)物理测试三、示例代码(1) busoff_recovery.c(2) busoff_recovery.h一、BusOff检测机制 (1)简介 CAN控制器可以判断出错误的类型是总线上暂时的数据错误(如外部干扰等…

Python中的内存管理:深入分析垃圾回收机制

python中有一个名为refchian的环状双向链表&#xff0c;python运行时创建的所有对象都会添加到refchain中。在refchain中的对象PyObject里都有一个ob_refcnt用来保存当前对象的引用计数器&#xff0c;就是该对象被引用的次数&#xff0c;当对象有新引用时ob_refcnt就会增加&…

西门子博途软件加密保护方法

一、程序块的专有技术保护 程序块的专有技术保护主要是对项目中的程序块&#xff08;OB、FB、FC、DB&#xff09;进行访问保护&#xff0c;如果没有专有技术保护密码则无法看到程序块中的具体内容&#xff0c;对于专有技术保护的 DB 块&#xff0c;如果没有密码只能读不能写。…

eNSP-OSPF协议其他区域不与骨干区域相连解决方法3

virtual-link技术 AR1 [ar1]int g0/0/0 [ar1-GigabitEthernet0/0/0]ip add 192.168.1.1 24 [ar1-GigabitEthernet0/0/0]quit [ar1]ospf [ar1-ospf-1]area 0 [ar1-ospf-1-area-0.0.0.0]net 192.168.1.0 0.0.0.255 [ar1-ospf-1-area-0.0.0.0]quit AR2 [ar2]int g0/0/0 [ar2-Gig…

skiaSharp linux 生成验码字体显示不出来

一、拷贝windows下的字体如&#xff1a;C:\Windows\Fonts 设置字体的地方&#xff1a; var fontPath Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Fonts", "TAHOMA.TTF");最终效果&#xff1a;

d3dx9_43.dll丢失怎么解决,四个解决方法帮你解决d3dx9_43.dll丢失

随着科技的不断发展&#xff0c;我们越来越依赖各种软件和硬件设备来提高生活和工作效率。然而&#xff0c;有时候我们可能会遇到一些技术问题&#xff0c;如“d3dx9_43.dll丢失”的问题。这个问题可能导致某些程序无法正常运行&#xff0c;给我们的生活带来诸多不便。因此&…

C++初阶--类与对象(1)

文章目录 类的引入类的定义访问限定符类成员的注意事项变量名的冲突 类的实例化类成员的声明与定义类的大小this指针特性 总结 类的引入 在c语言中&#xff0c;我们会这样写一个栈&#xff1a; struct Stack {int* a;int top;int capacity; };void StackInit(struct Stack* p…

线性代数2:梯队矩阵形式

图片来自 Europeana on Unsplash 一、前言 欢迎阅读的系列文章的第二篇文章&#xff0c;内容是线性代数的基础知识&#xff0c;线性代数是机器学习背后的基础数学。在我之前的文章中&#xff0c;我介绍了线性方程和系统、矩阵符号和行缩减运算。本文将介绍梯队矩阵形式&#xf…

Android 如何在Service中使用ViewModel

需求&#xff1a;最近有反馈说&#xff0c;需要在service中使用网络请求&#xff0c;而我网络请求就是封装的ViewModel。然后我就发现&#xff0c;原来service不支持&#xff0c;懵了呀&#xff01;哈哈 还是去看看ViewModel的源码了解下吧。下面有几个介绍的。就不多做赘述了。…

微信小程序开发之后台数据交互及wxs应用

目录 一、后端准备 1. 应用配置 2. 数据源配置 二、数据库 1. 创建 2. 数据表 3. 数据测试 三、前端 1. 请求方法整合 2. 数据请求 3. WXS的使用 4. 样式美化 5. 页面 一、后端准备 通过SpringMVC及mybatis的技术学习&#xff0c;还有前后端分离的技术应用&…

Mycat2 分布式数据库中间件

一.安装部署 Mycat2目前还不支持直接获取Docker镜像&#xff0c;需要自己通过Dockerfile打包镜像&#xff0c;其实这也是为了开发者考虑&#xff0c;比如一些个性化功能&#xff0c;如自定义分片等 Dockerfile FROM docker.io/adoptopenjdk/openjdk8:latestENV AUTO_RUN_DIR…

RemObjects Elements 12.0 Crack

Elements 是一个现代多功能软件开发工具链。 它支持六种流行的编程语言&#xff1a;Oxygene (Object Pascal)、C#、Java、Mercury (Visual Basic.NET™)、Go 和 Swift&#xff0c;适用于所有现代平台。 使用 Elements&#xff0c;您可以为您喜欢的任何平台进行编程- 无论是单…

想要精通算法和SQL的成长之路 - 找到最终的安全状态

想要精通算法和SQL的成长之路 - 找到最终的安全状态 前言一. 找到最终的安全状态1.1 初始化邻接图1.2 构建反向邻接图1.3 BFS遍历1.4 完整代码 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 找到最终的安全状态 原题链接 我们从题目中可以看出来&#xff1a; 出度为0的…

面试官:如何理解CDN?说说实现原理?

一、是什么 CDN (全称 Content Delivery Network)&#xff0c;即内容分发网络 构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c;降…

大模型技术实践(五)|支持千亿参数模型训练的分布式并行框架

在上一期的大模型技术实践中&#xff0c;我们介绍了增加式方法、选择式方法和重新参数化式方法三种主流的参数高效微调技术&#xff08;PEFT&#xff09;。微调模型可以让模型更适合于我们当前的下游任务&#xff0c;但当模型过大或数据集规模很大时&#xff0c;单个加速器&…

OpenCV中world模块介绍

OpenCV中有很多模块&#xff0c;模块间保持最小的依赖关系&#xff0c;用户可以根据自己的实际需要链接相关的库&#xff0c;而不需链接所有的库&#xff0c;这样在最终交付应用程序时可以减少总库的大小。但如果需要依赖OpenCV的库太多,有时会带来不方便&#xff0c;此时可以使…