百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Python实现基于地图四色原理的遗传算法(GA)自动着色

off999 2025-05-26 18:14 33 浏览 0 评论

本文介绍利用Python语言,实现基于遗传算法(GA)的地图四色原理着色操作。

1 任务需求

首先,我们来明确一下本文所需实现的需求。

现有一个由多个小图斑组成的矢量图层,如下图所示;我们需要找到一种由4种颜色组成的配色方案,对该矢量图层各图斑进行着色,使得各相邻小图斑间的颜色不一致,如下下图所示。

在这里,我们用到了四色定理(Four Color Theorem),又称四色地图定理(Four Color Map Theorem):如果在平面上存在一些邻接的有限区域,则至多仅用四种颜色来给这些不同的区域染色,就可以使得每两个邻接区域染的颜色都不一样。

2 代码实现

明确了需求,我们就可以开始具体的代码编写。目前国内各大博客中,有很多关于Python实现地图四色原理着色的代码,其中大多数是基于回溯法来实现的;而在一个英文博客网页中,看到了基于遗传算法的地图四色原理着色实现。那么就以该代码为例,进行操作。在这里,由于我本人对于遗传算法的理解还并不深入,因此在代码介绍方面或多或少还存在着一定不足,希望大家多多批评指正。

2.1 基本思路

遗传算法是一种用于解决最佳化问题的搜索算法,属于进化算法范畴。结合前述需求,首先可以将每一个区域的颜色作为一个基因,个体基因型则为全部地区(前述矢量图层共有78个小图斑,即78个区域)颜色基因的汇总;通过构建Rule类,将空间意义上的“相邻”转换为可以被遗传算法识别(即可以对个体基因改变加以约束)的信息;随后,结合子代的更替,找到满足要求的基因组;最终将得到的基因组再转换为空间意义上的颜色信息,并输出结果。

具体分步骤思路如下:

  1. 定义“规则”。“规则”用以将区域之间的空间连接情况转换为遗传算法可以识别的信息;被“规则”连接的两个区域在空间中是相邻的。
  2. 定义区域空间连接情况检查所需函数。这些函数用于检查两两区域之间的连接性是否满足逻辑;例如,若在“规则”中显示区域A与区域B连接,那么区域B也必须在“规则”中显示与区域A连接。
  3. 定义个体基因型。其中,各个体具有78个基因,每一个基因表示一个区域的颜色。
  4. 个体更替与最优基因选择。通过个体的不断更迭,选择出满足“规则”要求的个体基因型。
  5. 基因型解释。将得到的个体基因型进行解释,相当于第一步的反过程,即将基因信息转换为空间连接情况。
  6. 结果检查。检查所得到的颜色与最优个体基因组中的各个基因是否一致。

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个小区域的配色方案进行可视化。

相关推荐

电脑装了两个系统怎么切换(电脑安装2个系统怎么更换启动)

1.点击运行打开电脑点击左下角的开始菜单栏选项,右击鼠标在序列栏中选择运行打开。2.输入msconfig接着在运行的输入框中输入msconfig点击确定即可打开系统配置。3.点击引导打开系统配置的页面...

linux系统哪个版本好用(最好linux系统版本)

个人比较推荐Debian这个发行版本。DebianGNU/Linux于1993年首次公布,至今已经有近30年历史了。当然其他版本比如openSUSE,Slackware,ArchLinux,Ubu...

win10激活在哪里查看(win10激活时间在哪里看)

在Windows10中,您可以通过以下方法查看激活状态:方法1:使用“设置”应用1.点击屏幕左下角的“开始”按钮,然后点击“设置”(齿轮图标)。2.在设置窗口中,点击“系统”图标。3.在“系统...

官方win10dll文件修复工具(官方win7dll文件修复工具)

当电脑丢失dll文件时,可以采用以下几种方法进行一键修复:从回收站还原:如果是不小心误删了一些计算机文件,导致电脑出现异常的情况时,首先就可以去回收站找回dll文件,如果文件还在,就可以通过还原操作来...

qq所有历史旧版本大全(qq历史版本一览表)

有2种方法。一种是:你是QQ会员。你可以把旧版打开,聊天记录上传。然后打开新的QQ,下载。第2种是:你在硬盘上装了QQ软件,然后你就点卸载(uninst),把原来的卸了。然后按原位置覆盖,装上06版。...

电脑显示器分辨率怎么调(显示分辨率无法调整)

1、以win7为例,首先右键点击桌面,在右键菜单中直接显示了屏幕分辨率的选项,用鼠标点击一下这个选项。2、在分辨率设置选项页面中,有一个分辨率的选项,点击一下这个选项,上面默认显示的数值是你现在的屏幕...

8系统(8系统点检控制包含什么)

WIndows8系统是微软目前最新的操作系统,Moto的图形界面设计,使很多已经习惯于早期windows系统的用户难以接受,Windows8是一个向平板和桌面系统妥协的产物,存在着相当多的利弊。...

电脑软件管家(电脑软件管家在哪里找到)

电脑管家有着最大的安全云库,全新的杀毒引擎,深度清理电脑垃圾,为电脑重回巅峰状态,更有账号宝专版,10倍提升QQ防盗号能力,是很好用的。就自己而言,在电脑上用的是腾讯电脑管家这个第三方系统安全软件,管...

office2010破解(office2010破解密钥)
  • office2010破解(office2010破解密钥)
  • office2010破解(office2010破解密钥)
  • office2010破解(office2010破解密钥)
  • office2010破解(office2010破解密钥)
迅雷种子搜索器(迅雷种子搜索器手机版下载)

    迅雷种子搜索方法:    1.在开始菜单栏或者到文件的安装路径文件夹中找到P2P种子搜索器。&nb...

手机怎么解压文件(苹果手机怎么解压文件)

手机解压文件方法:1、首先,在手机中找到文件管理,打开文件管理。2、打开文件管理之后找到压缩包,然后打开。3、打开安装包之后,选择需要的文件,。4、接下来找到“解压至”,点击“解压至”。5、点击之后,...

虚拟机安装centos7(虚拟机安装centos7图形界面)

安装CentOS7在虚拟机中,您可以按照以下步骤操作:1.下载CentOS7的ISO映像文件。2.打开虚拟机软件(如VMware、VirtualBox等)并创建一个新的虚拟机。3.在虚拟机创...

电脑老是重启(电脑老是重启什么原因造成的)

电脑由于工作环境积尘与空气湿度过大,经常使主板的接插件部分受潮产生氧化;特别是内存条插座、PCI扩展槽、键盘鼠标接口、LOT接口、CMOS电池压盒、ATX电源插座等。一旦它们出现接触不良现象,很容易出...

win8系统怎么重装系统(win8.1系统重装教程)

1、修改Cortana资源占用:按Windows按钮,输入regedit,打开注册表编辑器,找到以下路径:HKEY_LOCAL_MACHINE—SYSTEM—CurrentControlSet—Ser...

手机163邮箱app下载(163邮箱下载手机版官网 app)

163邮箱登录首页入口为http://mail.163.com/网易163免费邮箱--中文邮箱第一品牌.容量自动翻倍,支持50兆附件,免费开通手机号码邮箱赠送3G超大附件服务.支持各种客户端软件收发,...

取消回复欢迎 发表评论: