图论解决复杂路口红绿灯安排,python语言实现

问题描述

说明性描述

说明性描述说明了需要解决的问题是什么,针对什么样的问题,期望什么样的解
一个交通路口实例的模型
这是一个5条路的交叉口,其中两条是单行线。这个图本身已经是实际问题的抽象,与行驶方向无关的因素如道路方位、宽度、车流量等都已被抽象去除。要求设计红绿灯,按不同方向行驶的车辆不能相互冲突,依次通过;在此基础上,要求红绿灯轮转次数最少
现在基本想法是对行驶方向分组

  • 同属一组的各个方向行驶的车辆,均可以同时行驶,不出现相互交错的行驶路线
  • 所做的分组应该尽可能大

操作性描述

操作性描述说明通过怎样的操作过程可以得到所要的解
采用图来进一步抽象问题,其中顶点集 V V V中的顶点元素表示所有可能可行方向,不难列出本例一共13个:AB, AC, AD, BA, BC, BD, DA, DB, DC, EA, EB, EC, ED。边集 E E E中的元素表示两个顶点所代表的可行方向有冲突。
行驶线路冲突图
有了冲突图,寻找安全分组的问题就可以换一种说法:为冲突图中的顶点确定一种分组,保证属于同一组的顶点互不邻接。

要解决的问题进一步抽象为图数据结构上的顶点分组

图着色问题

以非相邻为条件的最佳顶点分组问题,其实对应于有名的图最佳着色问题:把顶点看成区域,把相邻看成两个顶点有边界,把不同分组看做给相邻顶点以不同颜色着色。著名的四色问题表明,对于任一方式分割为区域的平面图,只需要4种颜色着色,就能保证相邻区域都具有不同颜色。

但是,从交叉路口构造出的冲突图可能不是平面图,因此完全可能需要更多的颜色。

图着色算法

现有的最佳着色算法基本相当于枚举所有可能的着色方案,从中选出使用颜色最少的方案。
下面考虑使用贪婪算法,或称贪心法。其基本思想是根据当时掌握的信息,尽可能地向得到解的方向前进,直到不能继续再换一个方向。这样做通常不能得到最优解,但能找到“可接受的”解,即给出一种较好的算法。
算法梗概(伪代码)如下:

输入:图G   #记录图中的顶点连接关系
集合verts保存G中的所有顶点   #建立初始状态
设置集合groups为空集   #记录得到的分组,元素是顶点集合
while 存在未着色的顶点:   #每次迭代用贪心法找一个新分组
    选一种颜色
    在未着色顶点中给尽量多的无互连边的点着色(构建一个分组)
    记录新分组的顶点组

# 算法结束时,groups里记录着一种分组方式
# 算法细节还需要进一步考虑

上面算法还有重要的细节要考虑:一种新颜色的着色处理。具体:

  • 图G保存需着色图中顶点的邻接信息;
  • 集合verts是图中所有尚未着色的顶点集合;
  • 用另一个变量new_group记录正在构造的用当前新颜色着色的顶点(一个顶点),在上面算法的每次迭代中重新构造,每次重新分组时将这个集合重新设置为空集。
 # 进一步整理着色问题。
new_group = 空集
for v in verts:
    if v与new_group中所有的顶点之间都没有边:
        从verts中去掉v
        把v加入new_group    

# 循环结束时,new_group中是可以用一种颜色着色的顶点集合。
# 用这段代码代替前面程序框架中主循环体里的一部分。

检查上面的算法,可以看到算法涉及一些集合操作图操作

  1. 构造空集
  2. 从集合中删除元素
  3. 向集合中加入元素
  4. 顺序获取集合里的各个元素(使用for循环)
  5. 获取图中所有顶点
  6. 判断两个顶点是否相邻

算法精化和python描述

算法细节处理:

  1. 如何表示颜色? 顺序整数;
  2. 如何记录得到的分组?用集合表示分组,把构造好的分组(集合)加入groups作为元素;
  3. 如何表示图结构?

python的集合操作:
10. 判断集合vs是否空集:not vs
11. 设置一个集合为空:vs = set()
12. 从集合中去掉一个元素:vs.remove(v)
13. 向集合中增加一个元素:vs.add(v)

python的集合数据类型不支持遍历,需要从当时的集合verts生成一个表,然后对表做遍历。
算法中需要的图操作依赖于,如何在python中实现图数据结构,这里假定两个与图有关的操作:

  1. 函数vertices(G)得到G中所有顶点的集合;
  2. 谓词 not_adjacent_with_set(v, group, G)检查顶点v与顶点集group中各顶点在图G中是否有边连接。

有了以上假设,就可以写程序:

python实现

def coloring(G): #做图G的着色
    color = 0
    groups = set()
    verts = vertices(G) #取得G图的所有顶点,依赖于图的表示
    while verts:
        new_group = set()
        for v in list(verts):
            if not_adjacent_with_set(v, newgroup, G):
                new_group.add(v)
                verts.remove(v)
            group.add((color, new_group))
            color += 1
    return groups

这个python函数能对任何一个图算出顶点分组。其中欠缺的细节就是图的表示,以及函数里设计的两个图操作

讨论

对于给定的交叉路口实例,可行的分组可能不唯一。算法给出的解是确定的,依赖于算法中选择顶点的具体策略,以及对图中顶点的遍历顺序,即**list(verts)**给出的顶点序列中的顺序。
回顾前面从问题出发做出一个python程序的工作过程:

  1. 有关工作开始于交叉路口的抽象图示,首先枚举出所有允许通行方向;
  2. 根据通行方向和冲突的定义,画出冲突图;
  3. 通行方向分组问题归结为冲突图中不相邻顶点的划分问题

问题是,这样的结果满足原交叉路口实例的需要吗?

仔细分析,上面算法中采取的定义不统一:

算法给出的结果是各分组互不相交,每个顶点只属于一个分组;而工作实际是只要求同属一组的顶点是互不冲突的,即允许顶点属于不同分组的情况

需要将之前得到的分组尽可能扩充,加入与已有分组不冲突的方向,如何修改前面算法,使之能给出这样分组?

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值