文章目录

  • 一、多目标优化算法简介
    • 1.基本知识
  • 二、NSGA2算法
    • 1.基本原理
    • 2.快速非支配排序
      • 2.1快速非支配排序 python实现
    • 3.拥挤距离
      • 3.1 拥挤距离python 实现
    • 4.精英选择策略
      • 4.1 精英选择策略python 实现
  • 总结

一、多目标优化算法简介

什么是多目标优化算法,见链接:
十分钟了解完多目标优化算法

1.基本知识

支配:假设小明9岁,50斤,小红8岁,45斤,小明无论是岁数还是体重都比小红大,所以小明支配小红。

互不支配:假设小明7岁,50斤,小红8岁,45斤,小明岁数比小红小,但体重比小红大,所以小明和小红互不支配。

帕累托集:在这个集合中,任意两个解互不支配。 如果有2个目标函数,帕累托集应该分布成一条曲线;如果有3个目标函数,帕累托集应该分布成一个超平面。 常规的2个目标函数,解法是目标加权得到是一个点,一个解;帕累托集,目标函数之间没有加权关系,所以得到是一条曲线。

非支配排序:将一组解分成n个集合:rank1,rank2…rankn,每个集合中所有的解都互不支配,但ranki中的任意解支配rankj中的任意解(i<j).

二、NSGA2算法

1.基本原理

多目标遗传算法是用来分析和解决多目标优化问题的一种进化算法,其核心就是协调各个目标函数之间的关系,找出使得各个目标函数都尽可能达到比较大的(或者比较小的)函数值的最优解集。在众多目标优化的遗传算法中,NSGA2算法是影响最大和应用范围最广的一种多目标遗传算法。在其出现后,由于它简单有效以及比较明显的优越性,使得该算法已经成为多目标优化问题中的基本算法之一。

介绍NSGA2,首先来介绍以下NSGA算法。

NSGA通过基于非支配排序的方法保留了种群中的优良个体,并且利用适应度共享函数保持了群体的多样性,取得了非常良好的效果。但实际工程领域中发现NSGA算法存在明显不足,这主要体现在如下3个方面:

  • (1)非支配排序的高计算复杂性。非支配排序算法一般要进行mN^3次搜索(m是目标函数的数目,体重和年龄可以被视为两个目标函数,N是一组解的大小),搜索的次数随着目标函数数量和种群大小的增加而增多。
  • (2)缺少精英策略。研究结果表明,引用精英策略可以加快遗传算法的执行,并且还助于防止优秀的个体丢失。
  • (3)需要指定共享参数share,在NSGA算法中保持种群和解的多样性方法都是依赖于共享的概念,共享的主要问题之一就是需要人为指定一个共享参数share。

为了克服非支配排序遗传算法(NSGA)的上述不足,印度科学家Deb于2002年在NSGA算法的基础上进行了改进,提出了带精英策略的非支配排序遗传算法(Elitist Non-Dominated Sorting Genetic Algorithm,NSGA-II),NSGA-II 算法针对NSGA的缺陷通过以下三个方面进行了改进[16]:

  • 提出了快速非支配的排序算法,降低了计算非支配序的复杂度,使得优化算法的复杂度由原来的 降为 ( 为目标函数的个数, 为种群的大小)。
  • 引入了精英策略,扩大了采样空间。将父代种群与其产生的子代种群组合在一起,共同通过竞争来产生下一代种群,这有利于是父代中的优良个体得以保持,保证那些优良的个体在进化过程中不被丢弃,从而提高优化结果的准确度。并且通过对种群所有个体分层存放,使得最佳个体不会丢失,能够迅速提高种群水平。
  • 引入拥挤度和拥挤度比较算子,这不但克服了NSGA算法中需要人为指定共享参数 的缺陷,而且将拥挤度作为种群中个体之间的比较准则,使得准Pareto域中的种群个体能均匀扩展到整个Pareto域,从而保证了种群的多样性。

2.快速非支配排序

解的支配关系与Pareto最优解



快速非支配排序步骤:
快速非支配排序就是将解集分解为不同次序的Pareto前沿的过程。
它可以描述为:

  • 1.为每个解p分配两个关键量:支配p的解个数n_p以及被p支配的解集S_p;
  • 2.设置i=1,将n_p=0的个体归入F_i;
  • 3.对于F_i中的个体,遍历每个解p的S_p,将其中每个解的n_p减1;
  • 4.i+=1,将n_p=0的解归入F_i;
  • 5.重复3、4,直到解集中所有个体都被归入某一个F_i。

2.1快速非支配排序 python实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu(余登武)
# @Date  : 2021/10/26
#@email:1344732766@qq.com
#============导入相关包========
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib; matplotlib.use('TkAgg')
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题#==========定义两个目标函数=============
# 定义函数1
def function1(x):value = -(x+2) ** 2+2*xreturn value
# 定义函数2
def function2(x):value = -(x - 2) ** 2return value#=========定义群体,并绘制初始解的分布图=====
pop_size = 10
max_gen = 100
# 迭代次数
#Initialization
min_x=-10
max_x=10
np.random.seed(10)#固定随机数种子,使每次生成的初始解集一样
solution=np.random.uniform(min_x,max_x,pop_size) #生成的初始解集#函数1 对应的初始目标函数值
values1=map(function1,solution) #python中的map用法,可以对遍历一个列表如solution,然后将列表元素传入到函数function1。得到解。得到的格式为对象
values1=[i for i in values1] #因为上一步得到是对象格式,需要转换成列表格式#函数2 对应的初始目标函数值
values2=map(function2,solution) #python中的map用法,可以对遍历一个列表如solution,然后将列表元素传入到函数function2。得到解。得到的格式为对象
values2=[i for i in values2] #因为上一步得到是对象格式,需要转换成列表格式plt.scatter(values1,values2, s=20, marker='o')
for i in range(pop_size):plt.annotate(i, xy=(values1[i], values2[i]), xytext=(values1[i] - 0.05, values2[i] - 0.05),fontsize=18)
plt.xlabel('function1')
plt.ylabel('function2')
plt.title('解的分布示意图')
plt.show()#=================快速非支配排序==============
'''1.n[p]=0 s[p]=[]
2.对所有个体进行非支配判断,若p支配q,则将q加入到S[p]中,并将q的层级提升一级。若q支配p,n[p]+1.
3.找出种群中np=0的个体,即最优解,并找到最优解的支配解集合。存放到front[0]中
4 i==0
5.判断front是否为空,若不为空,将front中所有的个体sp中对应的被支配个体数减去1,(存放np==0的解序号进front[i+1]);i=i+1,跳到2;若为空,则表明得到了所有非支配集合,程序结束
'''values=[values1,values2] #解集【目标函数1解集,目标函数2解集...】
def fast_non_dominated_sort(values):"""优化问题一般是求最小值:param values: 解集【目标函数1解集,目标函数2解集...】:return:返回解的各层分布集合序号。类似[[1], [9], [0, 8], [7, 6], [3, 5], [2, 4]] 其中[1]表示Pareto 最优解对应的序号"""values11=values[0]#函数1解集S = [[] for i in range(0, len(values11))]#存放 每个个体支配解的集合。front = [[]] #存放群体的级别集合,一个级别对应一个[]n = [0 for i in range(0, len(values11))]#每个个体被支配解的个数 。即针对每个解,存放有多少好于这个解的个数rank = [np.inf for i in range(0, len(values11))]#存放每个个体的级别for p in range(0, len(values11)):#遍历每一个个体# ====得到各个个体 的被支配解个数 和支配解集合====S[p] = [] #该个体支配解的集合 。即存放差于该解的解n[p] = 0  #该个体被支配的解的个数初始化为0  即找到有多少好于该解的 解的个数for q in range(0, len(values11)):#遍历每一个个体less = 0 #的目标函数值小于p个体的目标函数值数目equal = 0 #的目标函数值等于p个体的目标函数值数目greater = 0 #的目标函数值大于p个体的目标函数值数目for k in range(len(values)):  # 遍历每一个目标函数if values[k][p] > values[k][q]:  # 目标函数k时,q个体值 小于p个体less = less + 1  # q比p 好if values[k][p] == values[k][q]:  # 目标函数k时,p个体值 等于于q个体equal = equal + 1if values[k][p] < values[k][q]:  # 目标函数k时,q个体值 大于p个体greater = greater + 1  # q比p 差if (less + equal == len(values)) and (equal != len(values)):n[p] = n[p] + 1  # q比p,  比p好的个体个数加1elif (greater + equal == len(values)) and (equal != len(values)):S[p].append(q)  # q比p差,存放比p差的个体解序号#=====找出Pareto 最优解,即n[p]===0 的 个体p序号。=====if n[p]==0:rank[p] = 0 #序号为p的个体,等级为0即最优if p not in front[0]:# 如果p不在第0层中# 将其追加到第0层中front[0].append(p) #存放Pareto 最优解序号# =======划分各层解========"""#示例,假设解的分布情况如下,由上面程序得到 front[0] 存放的是序号1个体序号    被支配个数   支配解序号   front1          0            2,3,4,5    02,         1,          3,4,53,        1,           4,54,        3,           55          4,           0#首先 遍历序号1的支配解,将对应支配解[2,3,4,5] ,的被支配个数-1(1-1,1-1,3-1,4-1)得到表个体序号    被支配个数   支配解序号   front1          0            2,3,4,5    02,         0,          3,4,53,        0,           4,54,        2,           55          2,           0#再令 被支配个数==0 的序号 对应的front 等级+1得到新表..."""i = 0while (front[i] != []):  # 如果分层集合为不为空Q = []for p in front[i]:  # 遍历当前分层集合的各个个体pfor q in S[p]:  # 遍历p 个体 的每个支配解qn[q] = n[q] - 1  # 则将fk中所有给对应的个体np-1if (n[q] == 0):# 如果nq==0rank[q] = i + 1if q not in Q:Q.append(q)  # 存放front=i+1 的个体序号i = i + 1  # front 等级+1front.append(Q)del front[len(front) - 1]  # 删除循环退出 时 i+1产生的[]return front #返回各层 的解序号集合 # 类似[[1], [9], [0, 8], [7, 6], [3, 5], [2, 4]]front=fast_non_dominated_sort(values)#=================打印结果=======================
#遍历各层
for i in range(len(front)):print('第%d层,解的序号为%s'%(i,front[i]))jie=[]for j in front[i]:#遍历第i层各个解jie.append(solution[j])print('第%d层,解为%s'%(i,jie))

结果如图,发现成功找到最优解

3.拥挤距离

拥挤距离的定义
      在NSGA-II中,为了衡量在同一个前沿中各个解质量的优劣,作者为每个解分配了一个拥挤距离,其背后的思想是让求得的Pareto最优解在objective space中尽量分散,也就有更大可能让解在Pareto最优前沿上均匀分布。

拥挤距离主要是维持种群中个体的多样性。具体而言,一般来说 是指种群按照支配关系进行非支配排后单个Rank层中个体的密集程度。常用于支配关系的多目标算法中,例如NSGA-1I.

主要步骤如下:

  • 取单个前沿中个体按照一个目标上的值从小到大排序
  • 将最大目标值作为max,最小目标值保留作为min。并且这两个极值点的拥挤距离都被设置为inf即无穷大。因此注意,一个层中可能有多个具有inf的点,即如果层中有多个点在至少一个目标上相等,并且最大或最小,那么这些点的拥挤距离都是无穷大! !因为目标上呈现垂直的关系也是属于非支配的关系! !如果出现这种情况,说明你算法的多样性很烂! ~或者在某些算法早期可能出现这种情况
  • 在这个目标上计算每个个体最相邻个体之间的距离,即i-1和i+1的目标值的差。 并使用max和min对次值进行归一化。
  • 遍历目标,将目标上已经归一化的拥挤距离相加。
  • 进入下一层front前沿
  • 拥挤距离越大越好,最后按照拥挤距离重新排序各层,进而排序种群。

3.1 拥挤距离python 实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author: yudengwu(余登武)
# @Date  : 2021/10/27
#@email:1344732766@qq.com
#============导入相关包========
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib; matplotlib.use('TkAgg')
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题#==========定义两个目标函数=============
# 定义函数1
def function1(x):y = (x - 1) ** 2return y
# 定义函数2
def function2(x):y = np.cos(x)return y#=========定义群体,并绘制初始解的分布图=====
pop_size = 50
max_gen = 100
# 迭代次数
#Initialization
min_x=-10
max_x=10
np.random.seed(10)#固定随机数种子,使每次生成的初始解集一样
solution=np.random.uniform(min_x,max_x,pop_size) #生成的初始解集#函数1 对应的初始目标函数值
values1=map(function1,solution) #python中的map用法,可以对遍历一个列表如solution,然后将列表元素传入到函数function1。得到解。得到的格式为对象
values1=[i for i in values1] #因为上一步得到是对象格式,需要转换成列表格式#函数2 对应的初始目标函数值
values2=map(function2,solution) #python中的map用法,可以对遍历一个列表如solution,然后将列表元素传入到函数function2。得到解。得到的格式为对象
values2=[i for i in values2] #因为上一步得到是对象格式,需要转换成列表格式plt.scatter(values1,values2, s=20, marker='o')
for i in range(pop_size):plt.annotate(i, xy=(values1[i], values2[i]), xytext=(values1[i] - 0.05, values2[i] - 0.05),fontsize=18)
plt.xlabel('function1')
plt.ylabel('function2')
plt.title('解的分布示意图')
plt.show()#=================快速非支配排序==============
values=[values1,values2] #解集【目标函数1解集,目标函数2解集...】
def fast_non_dominated_sort(values):"""优化问题一般是求最小值:param values: 解集【目标函数1解集,目标函数2解集...】:return:返回解的各层分布集合序号。类似[[1], [9], [0, 8], [7, 6], [3, 5], [2, 4]] 其中[1]表示Pareto 最优解对应的序号"""values11=values[0]#函数1解集S = [[] for i in range(0, len(values11))]#存放 每个个体支配解的集合。front = [[]] #存放群体的级别集合,一个级别对应一个[]n = [0 for i in range(0, len(values11))]#每个个体被支配解的个数 。即针对每个解,存放有多少好于这个解的个数rank = [np.inf for i in range(0, len(values11))]#存放每个个体的级别for p in range(0, len(values11)):#遍历每一个个体# ====得到各个个体 的被支配解个数 和支配解集合====S[p] = [] #该个体支配解的集合 。即存放差于该解的解n[p] = 0  #该个体被支配的解的个数初始化为0  即找到有多少好于该解的 解的个数for q in range(0, len(values11)):#遍历每一个个体less = 0 #的目标函数值小于p个体的目标函数值数目equal = 0 #的目标函数值等于p个体的目标函数值数目greater = 0 #的目标函数值大于p个体的目标函数值数目for k in range(len(values)):  # 遍历每一个目标函数if values[k][p] > values[k][q]:  # 目标函数k时,q个体值 小于p个体less = less + 1  # q比p 好if values[k][p] == values[k][q]:  # 目标函数k时,p个体值 等于于q个体equal = equal + 1if values[k][p] < values[k][q]:  # 目标函数k时,q个体值 大于p个体greater = greater + 1  # q比p 差if (less + equal == len(values)) and (equal != len(values)):n[p] = n[p] + 1  # q比p,  比p好的个体个数加1elif (greater + equal == len(values)) and (equal != len(values)):S[p].append(q)  # q比p差,存放比p差的个体解序号#=====找出Pareto 最优解,即n[p]===0 的 个体p序号。=====if n[p]==0:rank[p] = 0 #序号为p的个体,等级为0即最优if p not in front[0]:# 如果p不在第0层中# 将其追加到第0层中front[0].append(p) #存放Pareto 最优解序号# =======划分各层解========i = 0while (front[i] != []):  # 如果分层集合为不为空Q = []for p in front[i]:  # 遍历当前分层集合的各个个体pfor q in S[p]:  # 遍历p 个体 的每个支配解qn[q] = n[q] - 1  # 则将fk中所有给对应的个体np-1if (n[q] == 0):# 如果nq==0rank[q] = i + 1if q not in Q:Q.append(q)  # 存放front=i+1 的个体序号i = i + 1  # front 等级+1front.append(Q)del front[len(front) - 1]  # 删除循环退出 时 i+1产生的[]return front #返回各层 的解序号集合 # 类似[[1], [9], [0, 8], [7, 6], [3, 5], [2, 4]]front=fast_non_dominated_sort(values)#=============拥挤距离================
def crowding_distance(values,front):""":param values: 群体[目标函数值1,目标函数值2,...]:param front: 群体解的等级,类似[[1], [9], [0, 8], [7, 6], [3, 5], [2, 4]]:return: front 对应的 拥挤距离"""distance = np.zeros(shape=(pop_size, ))  # 拥挤距离初始化为0for rank in front:  # 遍历每一层Pareto 解 rank为当前等级for i in range(len(values)):  # 遍历每一层函数值(先遍历群体函数值1,再遍历群体函数值2...)valuesi = [values[i][A] for A in rank]  # 取出rank等级 对应的  目标函数值i 集合rank_valuesi = zip(rank, valuesi)  # 将rank,群体函数值i集合在一起sort_rank_valuesi = sorted(rank_valuesi, key=lambda x: (x[1],x[0]))  # 先按函数值大小排序,再按序号大小排序sort_ranki = [j[0] for j in sort_rank_valuesi]  # 排序后当前等级ranksort_valuesi = [j[1] for j in sort_rank_valuesi]  # 排序后当前等级对应的 群体函数值i#print(sort_ranki[0],sort_ranki[-1])distance[sort_ranki[0]] = np.inf  # rank 等级 中 的最优解 距离为infdistance[sort_ranki[-1]] = np.inf  # rank 等级 中 的最差解 距离为inf#计算rank等级中,除去最优解、最差解外。其余解的拥挤距离for j in range(1, len(rank) - 2):distance[sort_ranki[j]] = distance[sort_ranki[j]] + (sort_valuesi[j + 1] - sort_valuesi[j - 1]) / (max(sort_valuesi) - min(sort_valuesi))  # 计算距离# 按照格式存放distancesdistanceA = [[] for i in range(len(front))]  #for j in range(len(front)):  # 遍历每一层Pareto 解 rank为当前等级for i in range(len(front[j])):  # 遍历给rank 等级中每个解的序号distanceA[j].append(distance[front[j][i]])return distanceAdistanceA=crowding_distance(values,front)#打印拥挤距离结果for i in range(len(front)):print('当前等级 解的序号为:',front[i])print('当前等级 解的拥挤距离为:',distanceA[i])

4.精英选择策略

多目标优化过程中,得到父辈和子代解后,我们需要选择优秀的群体进行下一代。
      这里采用精英选择策略

4.1 精英选择策略python 实现

#这里的front,distance,solution 数据格式同前文def elitism(self,front, distance, solution):"""精英选择策略:param front: 父代与子代 组合构成的解的等级:param distance:  父代与子代 组合构成的解 拥挤距离:param solution:  父代与子代 组合构成的解:return:  返回群体解。群体数量=(父代+子代)//2"""X1index = []  # 存储群体编号pop_size = len(solution) // 2  # 保留的群体个数 即(父辈+子辈)//2for i in range(len(front)):  # 遍历各层rank_distancei = zip(front[i], distance[i])  # 当前等级 与当前拥挤距离的集合sort_rank_distancei = sorted(rank_distancei, key=lambda x: (x[1], x[0]),reverse=True)  # 先按拥挤距离大小排序,再按序号大小排序,逆序sort_ranki = [j[0] for j in sort_rank_distancei]  # 排序后当前等级ranksort_distancei = [j[1] for j in sort_rank_distancei]  # 排序后当前等级对应的 拥挤距离iif (pop_size - len(X1index)) >=len(sort_ranki):  # 如果X1index还有空间可以存放当前等级i 全部解X1index.extend([A for A in sort_ranki])#print('已存放len(X1index)', len(X1index))#print('当前等级长度', len(sort_ranki))#print('需要存放的总长度,popsize)#num = pop_size-len(X1index)# X1index 还能存放的个数elif len(sort_ranki) > (pop_size-len(X1index)):  # 如果X1空间不可以存放当前等级i 全部解num = pop_size - len(X1index) X1index.extend([A for A in sort_ranki[0:num]])X1 = [solution[i] for i in X1index]return X1

总结

本文通定义两个目标函数,然后生成初始解。利用初始解,解释了核心知识点:快速非支配排序、拥挤距离、精英选择策略。本文的文字部分有借鉴他人博客。代码为自己纯手打。下一步,将快速非支配排序、拥挤距离、精英选择策略写进遗传算法,解决常规约束、带复杂约束问题(可能需要修改下快速非支配排序代码,因为复杂约束不仅仅考虑适应度,还有惩罚项在里面)构成多目标优化问题代码模板。
多目标遗传优化算法nsga2[python源码实现]
多目标遗传优化算法nsga2求解复杂约束问题【python源码实现】

经实验:快速非支配排序、拥挤距离、精英选择策略 无法应用于粒子群算法。因为无法定义pbest,gbest。

作者:电气-余登武。原创不易,禁止抄袭。

nsga2多目标优化之核心知识点(快速非支配排序、拥挤距离、精英选择策略)详解(python实现)相关推荐

  1. 【多目标优化求解】基于matlab非支配排序灰狼优化(NS-GWO)算法求解多目标优化问题【含Matlab源码 2015期】

    一.灰狼算法简介 1 前言: 灰狼优化算法(Grey Wolf Optimizer,GWO)由澳大利亚格里菲斯大学学者 Mirjalili 等人于2014年提出来的一种群智能优化算法.该算法受到了灰狼 ...

  2. 【优化调度】基于matlab非支配排序遗传算法求解车辆充电调度优化问题【含Matlab源码 2000期】

    ⛄一.遗传算法简介 由于国内外人民的生活方式和电动汽车主要购买人群的不同,国内外关于车辆充电调度问题研究的侧重点也不尽相同. 我国地大物博,人口众多,交通复杂.主要从以下方面研究: 1.通过经济手段或 ...

  3. 多目标优化NSGA-II(非支配排序常见于遗传算法)[1]

    目录 1 兴趣引入 2 多目标优化算法发展历史 3 非支配排序常见于遗传算法

  4. NSGAII快速非支配遗传算法二代学习笔记1

    Population种群 nondomination sort非支配排序  nondomination level非支配水平 solution解 rank=fitness 等级/适应度 recombi ...

  5. NSGA2多目标优化算法的MATLAB仿真

    目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 首先将一群具有多个目标的个体(解集,或者说线代里的向量形式)作为父代初始种群,在每一次迭代中,GA操 ...

  6. 【多目标优化】2. 非支配排序遗传算法 —(NSGA、NSGA-II)

    [多目标优化]1. 多目标优化的相关基本概念 [多目标优化]2. 非支配排序遗传算法 -(NSGA.NSGA-II) [多目标优化]3. 基于分解的多目标进化算法 -(MOEAD) 1. 非支配排序遗 ...

  7. [多目标优化算法]1.NSGA-II——非支配排序遗传算法

    笔者最近在学习有关多目标优化的内容,并对内容进行一些整理.这篇文章算是笔者的一篇个人学习笔记,也希望能对他人提供一定的帮助,若有不足之处,也欢迎指正和建议. 注:本文中所举例子均为最小化问题. 一.多 ...

  8. html锚点链接怎么互点,通过优化网页核心关键词快速提升排名

    网页核心关键词和网站页面的相关性程度,相关性好自然会有好的排名. 相关性是搜索引擎的核心之一,我们要想快速提升网站排名,必须要优化好网页核心关键词. 搜索引擎会通过标签.关键词密度.站内和站外的锚文本 ...

  9. 多目标优化算法:基于非支配排序的人工兔优化算法(Non-Dominated Sorting Artificial Rabbits Optimization ,NSARO)

    一.人工兔优化算法算法简介 人工兔优化算法(Artificial Rabbits Optimization ,ARO)由Liying Wang等人于2022年提出,该算法模拟了兔子的生存策略,包括绕道 ...

最新文章

  1. usaco Cow Tours 牛的旅行
  2. Random:产生随机数的类
  3. selenium 实现网页截图
  4. BZOJ 1977 [BeiJing2010组队]次小生成树 Tree
  5. android4.0 开机启动activity 4.0,如何正确理解和使用Activity的4种启动模式
  6. 多路平衡查找树 --- B(B-)树
  7. 情绪对使用产品的影响——读《设计心理学》
  8. mongodb备份还原
  9. 机器学习硕士、博士如何自救?
  10. 软件测试必读的七本书
  11. Azkaban流2.0
  12. python中文版下载32位-Python 2 32位
  13. Flutter 编译失败shared_preferences_macos
  14. 香橙派装php_【香橙派】安装宝塔面板 orange pi
  15. android 侧滑栏教程,Android控件开发——DrawerLayout侧滑菜单的实现
  16. html页面上promt,HTML DOM prompt()方法使用 - 全文
  17. 最有用的p d f 格式转换软件
  18. vue控制滚动条滑到某个位置
  19. IDEA修改默认的新建项目路径
  20. 利用网络编程实现TFTP协议

热门文章

  1. mysql 忘记root_解决MySQL忘记root密码
  2. 空间计量模型_Stata空间面板数据模型专题直播丨Stata空间计量3月远程直播
  3. 动态分辨率是什么意思_b站么么直播最新动态里都有啥 b站什么意思
  4. Spring Boot 内置Tomcat——集成PHP解决方案
  5. Navicat——MySQL之delimiter默认设置问题
  6. [USACO4.2]完美的牛栏The Perfect Stall
  7. java 酒店系统_基于JAVA的酒店管理系统
  8. iis里面的mime没有php扩展,IIS - 无后缀(无扩展名)的MIME类型配置
  9. 对二维数组自定义排序、Treemap自定义排序、priorityqueue自定义排序
  10. http从发出请求到接收响应的旅行