使用遗传算法解决图着色问题

简介: 图着色任务可以简单概括为:为图中的每个节点分配一种颜色,并保证相连接的节点对不会使用相同的颜色,同时,我们希望使用尽可能少的颜色。本文使用遗传算法解决图着色问题。

图着色

问题描述

在图论中,图是对象的结构化集合,用于表示对象对之间的关系。对象在图中表示为顶点(或节点),而一对对象之间的关系使用边表示:

绘图4.png

图是非常有用的对象,因为它们可以用于表示大量的现实生活中的结构、模式和关系,例如社交网络,电网布局,网站结构,计算机网络,原子结构等等。

图着色任务可以简单概括为:为图中的每个节点分配一种颜色,并保证相连接的节点对不会使用相同的颜色,下图显示了争取着色的图示例:

绘图5.png

在图着色问题中,我们通常希望使用尽可能少的颜色。例如,在上图中,可以使用三种颜色正确地对所示图进行着色。但是不可能仅使用两种颜色对其进行正确着色。从图论的角度而言,这意味着该图的色数(chromatic number)为3。

应用

许多现实生活中的问题都可以转化为图表示,并可以抽象为图着色问题。例如,为学生安排课程或为员工安排班次可以转换为图,其中相邻节点表示导致冲突的班级或班次。导致冲突的原因可能是同时上课的班级或连续的班次。由于此冲突,将同一个人分配给两个班级(或两个班次)将导致时间表无效。如果每种颜色代表不同的人,则将不同的颜色分配给相邻节点将解决冲突。同样,N皇后问题可以表示为图着色问题,其中图中的每个节点都代表棋盘上的正方形,而每对处于同一行、列或对角线的棋子通过边连接。其他相关应用包括对无线电台的频率分配,交通信号灯定时等等。

解的表示形式

可以使用整数列表表示图着色问题的解,其中每个整数代表一种颜色,而列表的每个元素都与图的节点之一匹配。

假设图中有10个节点,因此可以为每个节点分配0到9之间的索引。然后,使用10个元素的列表表示该图的节点颜色。例如:

(0, 2, 1, 3, 1, 2, 0, 3, 3, 0)

1. 使用了四种颜色,分别由整数0、1、2、3表示。

2. 第一、第七和第十个节点用第一种颜色着色。

3. 第三和第五节点用第二种颜色着色。

4. 第二和第六节点用第三种颜色着色。

5. 第四、第八和第九节点用第四颜色着色。

为了评估解决方案,需要遍历每对相连接的节点,并检查它们是否共享相同的颜色。将着色问题转换为违反颜色约束的问题,目标是将违反次数最小化,直到其为零,以实现图的正确着色。

同时还试图将使用的颜色数量减到最少。如果已知最小颜色数,我们可以使用与已知颜色数一样多的整数值。但是,多数情况下我们并没有此先验知识,一种解决方法是首先对使用的颜色数量进行估计。如果使用此数字找到合适的解决方案,则可以减少该数字,然后重试。如果未找到解决方案,则可以增加数量,然后重试直到找到能够找到解决方案的最小数量。可以通过使用软约束和硬约束来更快找到最小值。

图着色问题中的约束条件

首先定义硬约束和软约束:

1. 硬约束:为获得有效解而必须遵守的约束

2. 软约束:为获得最佳解而尽可能遵守的约束

在图着色问题中,颜色分配要求(其中两个相连接的节点不能具有相同颜色)是一个硬约束。必须将违反此约束的次数最小化为零,以获得有效的解。

将使用的颜色数量最小化作为一种软约束。我们希望最小化此数字,但不以违反硬约束为代价。

以高于估计值的颜色数开始算法流程,并使色数最小化,直到——理想情况下——达到实际的最小颜色数。通过创建成本函数来实现此方法,其中硬约束违反的成本大于违反软约束的成本。将总成本用作要最小化的适应度函数。

利用python实现问题创建

为了封装图形着色问题,创建名为 GraphColoringProblem 的 Python 类。

为了实现该类,需要利用 NetworkX 库,该库可以进行图的创建,处理和绘制。使用NetworkX 类的实例作为需要着色的图。除了可以从头开始创建图之外,还可以利用该库中预定义的图。

GraphColoringProblem 类的构造函数接受要着色的图形作为参数。此外,它接受hardConstraintPenalty 参数,该参数表示违反硬约束的惩罚因子。

# 导入所需库importnetworkxasnximportmatplotlib.pyplotaspltimportnumpyasnpclassGraphColoringProblem:
def__init__(self,graph,hardConstraintPenalty):
# 初始化实例变量self.graph=graphself.hardConstraintPenalty=hardConstraintPenalty# 创建图节点的列表self.nodeList=list(self.graph.nodes)
# 创建图的邻接矩阵self.adjMatrix=nx.adjacency_matrix(graph).todense()
def__len__(self):
"""        :return: the number of nodes in the graph        """returnnx.number_of_nodes(self.graph)
defgetCost(self,colorArrangement):
"""       计算给定颜色组合的总成本        """returnself.hardConstraintPenalty*self.getViolationsCount(colorArrangement) +self.getNumberOfColors(colorArrangement)
defgetViolationsCount(self,colorArrangement):
"""        计算给定颜色排列中违反颜色的次数        """iflen(colorArrangement) !=self.__len__():
raiseValueError("size of color arrangement should be equal to ", self.__len__())
violations=0# 遍历每对节点,查找它们之间是否存在链接并使用相同的颜色foriinrange(len(colorArrangement)):
forjinrange(i+1,len(colorArrangement)):
ifself.adjMatrix[i,j]:
ifcolorArrangement[i] ==colorArrangement[j]:
violations+=1returnviolationsdefgetNumberOfColors(self, colorArrangement):
"""        计算给定颜色排列使用的颜色数量        """returnlen(set(colorArrangement))
defplotGraph(self, colorArrangement):
"""        绘制具有根据给定颜色排列进行着色的图        """iflen(colorArrangement) !=self.__len__():
raiseValueError("size of color list should be equal to ",self.__len__())
# 创建唯一色列表colorList=list(set(colorArrangement))
# 创建实际颜色列表colors=plt.cm.rainbow(np.linspace(0,1,len(colorList)))
# 遍历节点,并根据颜色组合分配颜色colorMap= []
foriinrange(self.__len__()):
color=colors[colorList.index(colorArrangement[i])]
colorMap.append(color)
# 对相应节点进行着色nx.draw_kamada_kawai(self.graph,node_color=colorMap,with_labels=True)
returnplt

遗传算法解决图着色问题

常量及遗传算子定义

1. 导入所需库

fromdeapimportbasefromdeapimportcreatorfromdeapimporttoolsimportrandomimportnumpyasnpfrommatplotlibimportpyplotaspltimportseabornassnsimportnetworkxasnx

2. 硬约束惩罚因子

HARD_CONSTRAINT_PENALTY=10

3. 基因算法常量

POPULATION_SIZE=100P_CROSSOVER=0.9P_MUTATION=0.1MAX_GENERATIONS=100HALL_OF_FAME_SIZE=10MAX_COLORS=10

4. 实例化图着色问题,该实例具有要解决的所需NetworkX图,以及hardConstraintPenalty的所需值

gcp=GraphColoringProblem(nx.petersen_graph(),HARD_CONSTRAINT_PENALTY)

5. 定义最小化适应度策略

creator.create("FitnessMin",base.Fitness,weights=(-1.0,))

6. .由于解由代表参与颜色的整数值列表表示,因此需要定义一个随机生成器,该生成器将创建介于0和颜色数减1之间的整数。每个整数代表一种颜色。然后,定义解(个体)创建器,该创建器将生成随机整数的列表,列表的长度与给定图的长度匹配。最后,定义创建整个群体的运算符:

creator.create("Individual",list,fitness=creator.FitnessMin)
toolbox=base.Toolbox()
toolbox.register("Integers",random.randint,0,MAX_COLORS-1)
toolbox.register("individualCreator",tools.initRepeat,creator.Individual,toolbox.Integers,len(gcp))
toolbox.register("populationCreator",tools.initRepeat,list,toolbox.individualCreator)

7. 通过调用GraphColoringProblem类的getCost()方法,将适应度评估函数设置为计算解的违反颜色数和使用的颜色数量

defgetCost(individual):
returngcp.getCost(individual),
toolbox.register("evaluate",getCost)

8. 定义遗传算子

toolbox.register("select",tools.selTournament,tournsize=2)
toolbox.register("mate",tools.cxTwoPoint)
# mutUniformInt运算符,将给定的整数更改为允许范围内的另一个随机生成的整数toolbox.register("mutate",tools.mutUniformInt,low=0,up=MAX_COLORS-1,indpb=1.0/len(gcp))

使用精英主义策略

使用名人堂可以用来保留进化过程中种群中曾经存在的最佳个体,并不会由于选择,交叉或变异而失去了它们,HallOfFame类在tools模块中实现。

将Halloffame对象用于实现精英主义。 Halloffame对象中包含的个体被直接注入下一代,并且不受选择,交叉和突变的遗传算子的影响

# 名人堂成员数量HALL_OF_FAME_SIZE=30defeaSimpleWithElitism(population,
toolbox,
cxpb,
mutpb,
ngen,
stats=None,
halloffame=None,
verbose=__debug__):
"""    使用halloffame来实现精英机制。 包含在名人堂麦中的个体被直接注入下一代,并且不受选择,交叉和突变的遗传算子的影响。    """logbook=tools.Logbook()#用于监控算法运行,和统计数据logbook.header= ['gen','nevals'] + (stats.fieldsifstatselse [])
# 计算个体适应度invalid_ind= [indforindinpopulationifnotind.fitness.valid]
fitnesses=toolbox.map(toolbox.evaluate,invalid_ind)
forind,fitinzip(invalid_ind,fitnesses):
ind.fitness.values=fitifhalloffameisNone:
raiseValueError("halloffame parameter must not be empty!")
#更新名人堂成员halloffame.update(population)
hof_size=len(halloffame.items) ifhalloffame.itemselse0record=stats.compile(population) ifstatselse {}
logbook.record(gen=0,nevals=len(invalid_ind),**record)
ifverbose:
print(logbook.stream)
#开始遗传流程forgeninrange(1,ngen+1):
#选择个体数目=种群个体数-名人堂成员数offspring=toolbox.select(population,len(population) -hof_size)
#种群更新到下一代offspring=algorithms.varAnd(offspring,toolbox,cxpb,mutpb)
#计算个体适应度invalid_ind= [indforindinoffspringifnotind.fitness.valid]
fitnesses=toolbox.map(toolbox.evaluate,invalid_ind)
forind,fitinzip(invalid_ind,fitnesses):
ind.fitness.values=fit#将名人堂成员添加到当前代offspring.extend(halloffame.items)
#更新名人堂halloffame.update(offspring)
#使用当前代替换种群population[:] =offspring#将当前统计信息附加到日志record=stats.compile(population) ifstatselse {}
logbook.record(gen=gen,nevals=len(invalid_ind),**record)
ifverbose:
print(logbook.stream)
returnpopulation,logbook

遗传流程

defmain():
# 创建初始种群population=toolbox.populationCreator(n=POPULATION_SIZE)
# 定义监听统计数据stats=tools.Statistics(lambdaind: ind.fitness.values)
stats.register("min",np.min)
stats.register("avg",np.mean)
# 实例化名人堂类hof=tools.HallOfFame(HALL_OF_FAME_SIZE)
# 执行遗传流程population, logbook=eaSimpleWithElitism(population,
toolbox,
cxpb=P_CROSSOVER,
mutpb=P_MUTATION,
ngen=MAX_GENERATIONS,
stats=stats,
halloffame=hof,
verbose=True)
# 打印算法找到的最优解best=hof.items[0]
print("-- Best Individual = ",best)
print("-- Best Fitness = ",best.fitness.values[0])
print()
print("Number of colors = ",gcp.getNumberOfColors(best))
print("Number of violations = ",gcp.getViolationsCount(best))
print("Cost = ",gcp.getCost(best))
# 绘制最优解plt.figure(1)
gcp.plotGraph(best)
# 提取监听的统计结果minFitnessValues,meanFitnessValues=logbook.select("min","avg")
# 绘制统计结果plt.figure(2)
sns.set_style("whitegrid")
plt.plot(minFitnessValues, color='red')
plt.plot(meanFitnessValues, color='green')
plt.xlabel('Generation')
plt.ylabel('Min / Average Fitness')
plt.title('Min and Average fitness over Generations')
plt.show()

结果与分析

运行程序:

if__name__=="__main__":
main()

打印最佳解的相关信息:

-- Best Individual =  [0, 8, 3, 0, 3, 8, 0, 0, 3, 8]
-- Best Fitness =3.0
Number of colors =3Number of violations =0Cost =3

由于此图较为简单因此算法找到的最佳解就是本问题包含的最优解。

绘图6.png

最小适应度和平均适应度的变化:

绘图7.png

使用不同图测试算法效果

使用复杂图进行测试

gcp=GraphColoringProblem(nx.mycielski_graph(6), HARD_CONSTRAINT_PENALTY)

打印出算法找到的最优解

--BestIndividual=  [3, 6, 4, 4, 2, 3, 5, 4, 0, 6, 2, 1, 5, 0, 0, 1, 1, 6, 4, 4, 5, 1, 2, 1, 2, 3, 0, 5, 3, 6, 0, 5, 5, 2, 1, 0, 0, 1, 1, 3, 6, 1, 5, 1, 1, 3, 4]
--BestFitness=7.0Numberofcolors=7Numberofviolations=0Cost=7

由于知道该图的色数为6,因此尽管没有违反硬约束,是有效解但并不是最佳解。如果我们事先不知道着色数怎么办?一种解决方法是更改遗传算法的参数。例如,增加种群数量(可能还有HOF数量)或增加世代数。另一种方法是重新开始相同的搜索,但是减少颜色数量。由于该算法找到了具有7种颜色的解决方案,因此可以将最大颜色数减少为6种,并查看算法是否仍然可以找到有效的解决方案:

MAX_COLORS=6

MAX_COLORS = 6 时打印结果

-- Best Individual =  [0, 2, 0, 3, 4, 4, 5, 3, 5, 4, 0, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 4, 4, 2, 0, 2, 4, 5, 4, 5, 5, 2, 0, 0, 2, 3, 2, 2, 5, 5, 5, 5, 2, 0, 4, 1]
-- Best Fitness =6.0
Number of colors =6Number of violations =0

绘图8.png

最小适应度和平均适应度的变化:

绘图9.png

当我们将颜色的数量从10减少到6时,搜索空间将大大减少——在这种情况下,将从 1047 减少到 647 (因为图中有47个节点)——因此该算法更有可能找到最佳解,即使世代数和人口规模并未改变。因此,虽然算法的第一次运行可以使我们更接近最优解,但是最好不断减少颜色的数量,直到算法找不到更好的解决方案为止。

如果尝试将颜色的最大数量减少到5种,将始终存在至少遇到一种违反硬约束的情况。

MAX_COLORS=5

MAX_COLORS = 5时打印结果

--BestIndividual=  [1, 0, 1, 3, 2, 2, 2, 3, 3, 2, 1, 2, 0, 3, 4, 0, 2, 3, 3, 0, 2, 0, 1, 1, 4, 1, 4, 2, 2, 4, 4, 4, 2, 4, 4, 0, 4, 4, 0, 2, 4, 4, 4, 4, 0, 1, 3]
--BestFitness=15.0Numberofcolors=5Numberofviolations=1Cost=15
相关文章
|
6月前
|
算法 定位技术
【算法】 用Prolog解决地图着色问题
【算法】 用Prolog解决地图着色问题
90 0
|
6月前
|
数据可视化 算法 定位技术
Python实现地图四色原理的遗传算法(GA)着色实现
Python实现地图四色原理的遗传算法(GA)着色实现
|
机器学习/深度学习 传感器 算法
【配色优化】基于遗传算法进行图形着色优化附matlab代码
【配色优化】基于遗传算法进行图形着色优化附matlab代码
|
算法
|
算法 图形学
计算机图形学——实验五 基本图形学算法及着色器初步编程
实验五 基本图形学算法及着色器初步编程 1、 理解基本图形元素光栅化的基本原理,理解直线裁剪算法的原理; 2、 掌握直线的光栅化算法:DDA和Bresenham算法; 3、 掌握直线裁剪算法:Cohen-Surtherland算法; 1、 编程实现DDA算法和Bresenham算法生成直线。 2、 实现Cohen-Surtherland直线裁剪算法,调试、编译、修改程序。 要求: 根据所给的直线光栅化的示范源程序,在计算机上编译运行,输出正确结果(示范代码有错误,指出并改正)。
323 0
计算机图形学——实验五 基本图形学算法及着色器初步编程
|
1月前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
9天前
|
算法 数据安全/隐私保护 索引
OFDM系统PAPR算法的MATLAB仿真,对比SLM,PTS以及CAF,对比不同傅里叶变换长度
本项目展示了在MATLAB 2022a环境下,通过选择映射(SLM)与相位截断星座图(PTS)技术有效降低OFDM系统中PAPR的算法实现。包括无水印的算法运行效果预览、核心程序及详尽的中文注释,附带操作步骤视频,适合研究与教学使用。
|
17天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
18天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
|
19天前
|
存储 算法 决策智能
基于免疫算法的TSP问题求解matlab仿真
旅行商问题(TSP)是一个经典的组合优化问题,目标是寻找经过每个城市恰好一次并返回起点的最短回路。本文介绍了一种基于免疫算法(IA)的解决方案,该算法模拟生物免疫系统的运作机制,通过克隆选择、变异和免疫记忆等步骤,有效解决了TSP问题。程序使用MATLAB 2022a版本运行,展示了良好的优化效果。
下一篇
无影云桌面