基于遗传算法的地图四色原理绘图上色的Python代码
本文介绍利用Python语言,实现基于遗传算法(GA)的地图四色原理着色操作。
1 任务需求
首先,我们来明确一下本文所需实现的需求。
现有一个由多个小图斑组成的矢量图层,如下图所示。
我们需要找到一种由4种颜色组成的配色方案,对该矢量图层各图斑进行着色,使得各相邻小图斑间的颜色不一致,如下图所示。
在这里,我们用到了四色定理(Four Color Theorem),又称四色地图定理(Four Color Map Theorem):如果在平面上存在一些邻接的有限区域,则至多仅用四种颜色来给这些不同的区域染色,就可以使得每两个邻接区域染的颜色都不一样。
2 代码实现
明确了需求,我们就可以开始具体的代码编写。目前国内各大博客中,有很多关于Python实现地图四色原理着色的代码,其中大多数是基于回溯法来实现的;而在一个英文博客网页中,看到了基于遗传算法的地图四色原理着色实现。那么就以该代码为例,进行操作。在这里,由于我本人对于遗传算法的理解还并不深入,因此在代码介绍方面或多或少还存在着一定不足,希望大家多多批评指正。
2.1 基本思路
遗传算法是一种用于解决最佳化问题的搜索算法,属于进化算法范畴。结合前述需求,首先可以将每一个区域的颜色作为一个基因,个体基因型则为全部地区(前述矢量图层共有78
个小图斑,即78
个区域)颜色基因的汇总;通过构建Rule
类,将空间意义上的“相邻”转换为可以被遗传算法识别(即可以对个体基因改变加以约束)的信息;随后,结合子代的更替,找到满足要求的基因组;最终将得到的基因组再转换为空间意义上的颜色信息,并输出结果。
具体分步骤思路如下:
- 定义“规则”。“规则”用以将区域之间的空间连接情况转换为遗传算法可以识别的信息;被“规则”连接的两个区域在空间中是相邻的。
- 定义区域空间连接情况检查所需函数。这些函数用于检查两两区域之间的连接性是否满足逻辑;例如,若在“规则”中显示区域
A
与区域B
连接,那么区域B
也必须在“规则”中显示与区域A
连接。 - 定义个体基因型。其中,各个体具有
78
个基因,每一个基因表示一个区域的颜色。 - 个体更替与最优基因选择。通过个体的不断更迭,选择出满足“规则”要求的个体基因型。
- 基因型解释。将得到的个体基因型进行解释,相当于第一步的反过程,即将基因信息转换为空间连接情况。
- 结果检查。检查所得到的颜色与最优个体基因组中的各个基因是否一致。
2.2 代码讲解
接下来,将完整代码进行介绍。其中,shapefile_path
即为矢量图层的保存路径;"POLY_ID_OG"
则为矢量图层的属性表中的一个字段,其代表每一个小图斑的编号。
# -*- coding: utf-8 -*-
"""
Created on Sun Oct 31 19:22:33 2021
@author: Chutj
"""
import genetic
import unittest
import datetime
from libpysal.weights import Queen
shapefile_path="G:/Python_Home1/stl_hom_utm.shp"
weights=Queen.from_shapefile(shapefile_path,"POLY_ID_OG")
one_neighbor_other=weights.neighbors
# 定义“规则”,用以将区域之间的空间连接情况转换为遗传算法可以识别的信息。被“规则”连接的两个区域在空间中是相邻的
class Rule:
Item = None
Other = None
Stringified = None
def __init__(self, item, other, stringified):
self.Item = item
self.Other = other
self.Stringified = stringified
def __eq__(self, another):
return hasattr(another, 'Item') and \
hasattr(another, 'Other') and \
self.Item == another.Item and \
self.Other == another.Other
def __hash__(self):
return hash(self.Item) * 397 ^ hash(self.Other)
def __str__(self):
return self.Stringified
# 定义区域空间连接情况检查所需函数,用以确保区域两两之间相邻情况的准确
def buildLookup(items):
itemToIndex = {}
index = 0
for key in sorted(items):
itemToIndex[key] = index
index += 1
return itemToIndex
def buildRules(items):
itemToIndex = buildLookup(items.keys())
rulesAdded = {}
rules = []
keys = sorted(list(items.keys()))
for key in sorted(items.keys()):
keyIndex = itemToIndex[key]
adjacentKeys = items[key]
for adjacentKey in adjacentKeys:
if adjacentKey == '':
continue
adjacentIndex = itemToIndex[adjacentKey]
temp = keyIndex
if adjacentIndex < temp:
temp, adjacentIndex = adjacentIndex, temp
ruleKey = str(keys[temp]) + "->" + str(keys[adjacentIndex])
rule = Rule(temp, adjacentIndex, ruleKey)
if rule in rulesAdded:
rulesAdded[rule] += 1
else:
rulesAdded[rule] = 1
rules.append(rule)
for k, v in rulesAdded.items():
if v == 1:
print("rule %s is not bidirectional" % k)
return rules
# 定义颜色所代表的基因组
colors = ["Orange", "Yellow", "Green", "Blue"]
colorLookup = {}
for color in colors:
colorLookup[color[0]] = color
geneset = list(colorLookup.keys())
# 定义个体基因型,其中各个体有78个基因,每一个基因代表一个区域。个体基因需要满足“规则”中相邻的区域具有不同的颜色
class GraphColoringTests(unittest.TestCase):
def test(self):
rules = buildRules(one_neighbor_other)
colors = ["Orange", "Yellow", "Green", "Blue"]
colorLookup = {}
for color in colors:
colorLookup[color[0]] = color
geneset = list(colorLookup.keys())
optimalValue = len(rules)
startTime = datetime.datetime.now()
fnDisplay = lambda candidate: display(candidate, startTime)
fnGetFitness = lambda candidate: getFitness(candidate, rules)
best = genetic.getBest(fnGetFitness, fnDisplay, len(one_neighbor_other), optimalValue, geneset)
self.assertEqual(best.Fitness, optimalValue)
keys = sorted(one_neighbor_other.keys())
for index in range(len(one_neighbor_other)):
print(keys[index]," is ",colorLookup[best.Genes[index]])
# 输出各区域颜色
def display(candidate, startTime):
timeDiff = datetime.datetime.now() - startTime
print("%s\t%i\t%s" % (''.join(map(str, candidate.Genes)), candidate.Fitness, str(timeDiff)))
# 检查各区域颜色是否与个体基因所代表的颜色一致
def getFitness(candidate, rules):
rulesThatPass = 0
for rule in rules:
if candidate[rule.Item] != candidate[rule.Other]:
rulesThatPass += 1
return rulesThatPass
# 运行程序
GraphColoringTests().test()
2.3 结果展示
执行上述代码,即可得到结果。在这里值得一提的是:这个代码不知道是其自身原因,还是我电脑的问题,执行起来非常慢——单次运行时间可能在5 ~ 6个小时左右,实在太慢了;大家如果感兴趣,可以尝试着能不能将代码的效率提升一下。
代码执行完毕后得到的结果是文字形式的,具体如下图所示。
可以看到,通过203
次迭代,找到了满足要求的地图配色方案,用时06小时06分钟;代码执行结果除显示出具体个体的整体基因型之外,还将分别显示78
个小区域(小图斑)各自的具体颜色名称(我上面那幅图没有截全,实际上是78
个小区域的颜色都会输出的)。
当然,大家也可以发现,这种文字表达的代码执行结果显然不如直接来一幅如下所示的结果图直观。但是,由于代码单次执行时间实在是太久了,我也没再腾出时间(其实是偷懒)对结果的可视化加以修改。大家如果感兴趣的话,可以尝试对代码最终的结果呈现部分加以修改——例如,可以通过Matplotlib库的拓展——Basemap
库将78
个小区域的配色方案进行可视化。
至此,大功告成。
基于遗传算法的地图四色原理绘图上色的Python代码的更多相关文章
- POJ-1129 DFS染色+四色原理的应用
OJ-ID: POJ-1129 author: Caution_X date of submission: 20190927 tags: DFS+四色原理的应用 descri ...
- 决策树ID3原理及R语言python代码实现(西瓜书)
决策树ID3原理及R语言python代码实现(西瓜书) 摘要: 决策树是机器学习中一种非常常见的分类与回归方法,可以认为是if-else结构的规则.分类决策树是由节点和有向边组成的树形结构,节点表示特 ...
- .NET应用架构设计—四色原型模式(色彩造型、域无关的模型)(概念版)
阅读文件夹: 1.背景介绍 2.问自己,UML对你来说有意义吗?它帮助过你对系统进行分析.建模吗? 3.一直以来事实上我们被一个缝隙隔开了,使我们对OOAD遥不可及 4.四色原型模式填补这个历史缝隙, ...
- .NET应用架构设计—面向对象分析与设计四色原型模式(彩色建模、领域无关模型)(概念版)
阅读目录: 1.背景介绍 2.问自己,UML对你来说有意义吗?它帮助过你对系统进行分析.建模吗? 3.一直以来其实我们被一个缝隙隔开了,使我们对OOAD遥不可及 4.四色原型模式填补这个历史缝隙,让我 ...
- 【转】基于RSA算法实现软件注册码原理初讨
1 前言 目前,商用软件和共享软件绝大部份都是采用注册码授权的方式来保证软件本身不被盗用,以保证自身的利益.尽管很多常用的许多软件系统的某些版本已经被别人破解,但对于软件特殊行业而言,注册码授权的方式 ...
- DDD:四色原型中Role的 “六” 种实现方式和PHP的Swoole扩展
目录 背景六种实现方式第一种:未显式体现角色的模式.第二种:使用“显式接口”显式体现角色的模式.第三种:使用“扩张方法”显式体现角色的模式.第四种:使用“领域服务”显式体现角色的模式.第五种:使用“包 ...
- 【科普】Web(瓦片)地图的工作原理
[译者按:在看MapBox Guides文档时,看到这篇 How do web maps work?,这篇文档通俗易懂地阐述了Web地图是如何工作的,其实更偏向讲瓦片地图的工作原理,鉴于之前很多人不了 ...
- 基于遗传算法(Genetic Algorithm)的TSP问题求解(C)
基于遗传算法的TSP问题求解(C) TSP问题: TSP(Travelling salesman problem): 译作“旅行商问题”, 一个商人由于业务的需要,要到n个城市,每个城市之间都有一条路 ...
- DDD:四色原型中Role的 “六” 种实现方式
背景 一个实体在不同的上下文中具备不同的职责,如:产品在“生产完成上下文”中具备的一些职责,在“质检相关上下文”中具备另外一些职责.四色原型.DIC和“UML事物模式”在不同的维度阐述了这一情况,在代 ...
- 【百度地图开发之二】基于Fragment的地图框架的使用
写在前面的话: [百度地图开发之二]基于Fragment的地图框架的使用(博客地址:http://blog.csdn.net/developer_jiangqq),转载请注明. Author:hmji ...
随机推荐
- ubuntu20.04修改静态ip不生效问题
一.前言 最近从头开始配置hadoop的时候,由于想切换到NAT模式下配置hadoop,但在修改ip的时候发现设置了静态ip,但ip不生效,查了很多资料,发现由于配置信息写错了. 二.解决问题 ifc ...
- Kubernetes集群YAML文件详解
Kubernetes集群YAML文件详解 概述 k8s 集群中对资源管理和资源对象编排部署都可以通过声明样式(YAML)文件来解决,也就是可以把需要对资源对象操作编辑到YAML 格式文件中,我们把这种 ...
- 谈软件-Java重构案例之Switch_Statements
1.软件重构,大量swich语句如何重构 2.使用 ide 使用 快捷键ctrl+alt+shift+T调出重构菜单,选择method对之前的for循环重构一个method 3.得到一个新的方法,使用 ...
- pyinstaller打包TVM/RPC相关脚本及DSO文件
0. 创建anaconda env numpy中MKL/BLAS库占用很大空间.使用如下命令创建新环境,并替换numpy. conda create -n extranumpy python=3.8. ...
- chrome设置socket5代理
利用自带的参数命令打破一个死循环. chrome可执行文件 --show-app-list --proxy-server="SOCKS5://127.0.0.1:1080"
- css初始化收集
页面元素样式初始化 * { margin: 0; padding: 0; box-sizing: border-box; } html { font-size: 100px; } /* 去掉a链接的文 ...
- NLP实践!文本语法纠错模型实战,搭建你的贴身语法修改小助手 ⛵
作者:韩信子@ShowMeAI 深度学习实战系列:https://www.showmeai.tech/tutorials/42 自然语言处理实战系列:https://www.showmeai.tech ...
- Android 内存缓存框架 LruCache 的实现原理,手写试试?
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在之前的文章里,我们聊到了 LRU 缓存淘汰算法,并且分析 Java 标准库中支持 ...
- 【QT开发问题】使用自定义的QGroupBox,重写绘图事件paintEvent后边框消失的问题
问题描述 Qt界面开发过程中,使用自定义的QGroupBox,重写绘图事件paintEvent时,出现边框被覆盖的情况,或边框消失的问题. 左图是原始状态,直接重写绘图事件后,会变成右图空白状态. ...
- avue框架 拼接后端返回的数据到table中
根据要求展示下列详细地址情况: 后端返回的数据: 具体实现步骤: { label: "详细地址", prop: "buildingName", display: ...