数据结构之图:加权无向图与寻找最小生成树,Python——27

加权无向图与prim算法和Kruskal算法寻找最小生成树

加权无向图的介绍

引入

  • 加权无向图是一种为每条边关联一 个权重值或 是成本的图模型。这种图能够自然地表示许多应用。在一副航空图中,边表示航线,权值则可以表示距离或是费用。在一副电路图中,边表示导线,权值则可能表示导线的长度即成本,或是信号通过这条先所需的时间。此时我们很容易就能想到,最小成本的问题,例如,从西安飞纽约,怎样飞才能使时间成本最低或者是金钱成本最低?
    在这里插入图片描述
  • 给图的边赋予一定的权重标准,构成加权无向图

加权无向图中的边我们就不能简单向之前一样使用v-w两个顶点表示了(之前的adj_list列表中存放的是顶点,而必须要给边关联一个权重值,因此我们可以使用对象来描述一条边(现在要存放边对象Edge)。

构造加权无向边

属性及方法设计
  1. 构造方法__init__()中
    vertex1和vertex2分别表示要构造的边的两端的顶点;weight表示要构造的边的权重
  2. get_weight()获取边的权重
  3. either() 获取当前边的其中一个顶点
  4. opposite(v) 获取指定顶点的相反方向的顶点(以当前边对象Edge为媒介,存在唯一的相反方向的顶点),一般用在获取了一个顶点之后
  5. compare(edge) 比较传入的Edge对象的权重和自身的权重,自身权重大返回1,两者相等返回0,否则返回-1

加权无向边Python代码实现
class Edge:def __init__(self, vertex1, vertex2, weight):self.vertex1 = vertex1self.vertex2 = vertex2self.weight = weightdef get_weight(self):return self.weightdef either(self):"""Return either vertex of this Edge"""return self.vertex1def opposite(self, v):return self.vertex1 if v == self.vertex2 else self.vertex2def compare(self, edge):return 1 if self.weight > edge.weight else (-1 if self.weight < edge.weight else 0)

代码较简单,测试就略过了,这里实现的Edge对象将用于后面的类方法实现。

Prim算法跳转
Kruskal算法跳转

Python代码实现加权无向图

from Structure.graph.Edge import Edgeclass WeightedUndigraph:def __init__(self, v):self.num_vertices = vself.num_edges = 0self.adj_list = [[] for _ in range(v)]def get_num_vertices(self):return self.num_verticesdef get_num_edges(self):return self.num_edgesdef add_edge(self, edge: Edge):v1 = edge.either()v2 = edge.opposite(v1)self.adj_list[v1].append(edge)self.adj_list[v2].append(edge)self.num_edges += 1def adjacent_edges(self, vertex):return self.adj_list[vertex]def all_edges(self):all_edges = []for i in range(self.num_vertices):for edge in self.adj_list[i]:if edge.opposite(i) < i:      all_edges.append(edge)return all_edges

最小生成树的介绍

引入
举例

  • 省领导审查全省各市,如何找出最优最快路径完成审查
  • 所有连接这些城市的道路中,路径(权重)最短的则是最优路径,此时要用到最小生成树

最小生成树的定义

  • 最小生成树(Minimum spanning tree)或说最小权重生成树(Minimum weight spanning tree),是一幅连通的加权无向图中所有的边集合的一个子集,并且这些边必须连接所有顶点,这个子集它必须是无环的、权重和是最小的
  • 对于连通图,一个图中可能有多颗生成树,最小生成树其实是最小权重生成树的简称。
  • 广义上而言,对于非连通无向图来说,它的每一连通分量同样有最小生成树,它们的并被称为最小生成森林。

约定

  1. 只考虑连通图。最小生成树的定义说明它只能存在于连通图中,如果图不是连通的,那么分别计算每个连通图子图的最小生成树,合并到一起称为最小生成森林。
    因为非连通图,无法找出连接全部顶点的边
  2. 所有边的权重都各不相同。如果不同的边权重可以相同,那么一 副图的最小生成树就可能不唯一 了 ,虽然我们的算法可以处理这种情况,但为了好理解,我们约定所有边的权重都各不相同。

最小生成树的性质

  • 用一条边连接最小生成树中的任意两个(不相邻的)顶点都会产生一 个新的环;
    在这里插入图片描述
  • 删除最小生成树的任意一条边,将会得到两棵独立的树;
    在这里插入图片描述

寻找最小生成树(Minimum Spanning Tree)

在实现寻找最小生成树之前,我们先来了解一下实现寻找最小生成树的两个原理

切分定理

切分定理
先了解概念
要从一副连通图中找出该图的最小生成树,需要通过切分定理完成。

  • 切分:将图的所有顶点按照某些规则分为两个非空且没有交集的集合。
  • 横切边:连接两个属于不同集合的顶点的边称之为横切边。
    例如我们将图中的顶点切分为两个集合,色顶点于一个集合,色顶点属于外一个集合,那么效果如下:

再了解定义
切分定理:

  • 在一副加权图中, 给定任意的切分,它的横切边中的权重最小者必然属于图中的最小生成树。
  • 注意:一次切分产生的多个横切边中,权重最小的边不一定是所有横切边中唯一属于图的最小生成树的边。也有可能非最小权重的横切边也是最小生成树的边

贪心算法(Greedy Algorithm)

定义

  • 任何在想要寻找全局最优方法时,在所有单个阶段进行探索时都尝试寻找当前阶段的局部最优解,以尝试获取最终的全局最优解,这样的算法都可以称之为贪心算法。
  • 在许多问题中,贪心算法的思想并不能总是获得一个最优解,但是贪心算法的探索过程,会生成许多的局部最优解,这些局部最优解在合理的时间内,也可以近似地等于一个全局最优解

Prim算法

计算图的最小生成树的算法有很多种,但这些算法都可以看做是贪心算法的一种特殊情况,这些算法的不同之处在于保存切分和判定权重最小的横切边的方式。

prim算法定义

  • prim算法是一种用于寻找加权无向图中最小生成树的贪心算法,prim算法选择随机的一个顶点开始,把这个顶点当做处于MST的集合,而把其他不在这颗MST中的顶点标记为不处于MST中的集合,然后通过不断的重复做某些操作,可以逐渐将非最小生成树中的顶点加入到最小生成树中,直到所有的顶点都加入到最小生成树中。

Prim算法的切分规则:

  • 把最小生成树中的顶点看做是一个集合 ,把不在最小生成树中的顶点看做是另外一个集合。
  • 最开始是把任意一个结点当做最小生成树的结点;而剩下的其他结点则不是树中的结点
    在这里插入图片描述

在实现prim算法寻找MST之前,我们要借助一个最小索引优先队列,来方便地取出最小权重边:

  • 引入索引最小优先队列the_cut_edges,它的索引代表图的顶点,储存的值代表从其他的某个顶点到当前索引对应顶点的边的权重,当遍历完某个顶点的邻接边时,the_cut_edges就会储存所有该顶点通向其他顶点的边的权重(重复的权重后面会有设计方法来避免),当最后遍历完时,我们只需要调用索引最小优先队列的delete_min_and_get_index()即可获取最小权重对应的顶点
    在这里插入图片描述

索引最小有限队列代码传送门
加权无向边,up↑
加权无向图,up↑

Prim算法Python代码实现寻找MST

from Structure.graph.WeightedUndigraph import WeightedUndigraph
from Structure.graph.Edge import Edge
from Structure.PriorityQueue.IndexMinpriorutyQueue import IndexMinPriorityQueue
from math import infclass PrimMST:def __init__(self, graph):"""MST here represent the Minimum Spanning Tree of the current loop"""self.graph = graph# Memorize the cheapest edge to MST of each vertex(index)self.min_edge_to_MST = [None for _ in range(self.graph.get_num_vertices())]# Store the smallest weight of each vertex(index)'s edge to MST;# Initialize it with infinite plus, we will compare out a minimum weight afterself.min_weight_to_MST = [+inf for _ in range(self.graph.get_num_vertices())]# Mark a True if a vertex(index) has been visitedself.marked = [False for _ in range(self.graph.get_num_vertices())]# Memorize the smaller weight of each vertex(index)'s edge connected to MSTself.the_cut_edges = IndexMinPriorityQueue(self.graph.get_num_vertices())# Initialize a 0.0 as the minimum weight to weight_to_MSTself.min_weight_to_MST[0] = 0.0self.the_cut_edges.insert(0, 0.0)while not self.the_cut_edges.is_empty():# Take out the minimum-weighted vertex, and make a visit(update) for itself.visit(self.the_cut_edges.delete_min_and_get_index())def visit(self, v):"""Update the MST"""self.marked[v] = Truefor e in self.graph.adjacent_edges(v):w = e.opposite(v)# Check if the opposite vertex of v in edge e is marked, if did, skip this loopif self.marked[w]:continue# Find out the minimum-weighted-edge vertex opposite to this vertex(v)if e.get_weight() < self.min_weight_to_MST[w]:    # e.get_weight():Get weight of the edge between v and w# Update the minimum edge and weight# print(f"v: {v}, w: {w}, min_weight_edge: {e.get_weight()}")self.min_edge_to_MST[w] = eself.min_weight_to_MST[w] = e.get_weight()if self.the_cut_edges.is_index_exist(w):# print(w, e.get_weight())self.the_cut_edges.change_item(w, e.get_weight())else:self.the_cut_edges.insert(w, e.get_weight())def min_weight_edges(self):return [edge for edge in self.min_edge_to_MST if edge]if __name__ == '__main__':with open('../MST.txt', 'r') as f:num_vertices = int(f.readline())num_edges = int(f.readline())graph = WeightedUndigraph(num_vertices)for e in range(num_edges):v1, v2, w = f.readline().split()graph.add_edge(Edge(int(v1), int(v2), float(w)))P_MST = PrimMST(graph)for e in P_MST.min_weight_edges():v = e.either()w = e.opposite(v)weight = e.weightprint(f"v: {v} w: {w} weight: {weight}")

运行结果

v: 1 w: 7 weight: 0.19
v: 0 w: 2 weight: 0.26
v: 2 w: 3 weight: 0.17
v: 4 w: 5 weight: 0.35
v: 5 w: 7 weight: 0.28
v: 6 w: 2 weight: 0.4
v: 0 w: 7 weight: 0.16

即是最小生成树的所有边的权重和

MST.txt

8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93

Kruskal算法

定义

  • kruskal算法图论中寻找加权无向图的最小生成树的另一种贪心算法,它的主要思想是按照边的权重(从小到大)处理它们,将边加入最小生成树中,加入的边不会与已经加入最小生成树的边构成环,直到树中含有N-1条边为止。

kruskal算法和prim算法的区别:

  • Prim算法是一次一条边的构造最小生成树,每一步都为一棵树添加一条边。kruskal算法构造最小生成树的时候也是一次一条边地构造,但它的切分规则是不一样的。它每一次寻找的边会连接一片森林中的两棵树。如果一副加权无向图由V个顶点组成 ,初始化情况下每个顶点都构成一棵独立的树,则V个顶点对应V棵树,组成一片森林, kruskal算法每一次处理都会将两棵树合并为一棵树,直到整个森林中只剩一棵树为止。

实现步骤:
在这里插入图片描述

Kruskal算法在寻找MST时,需要用到并查集来实现,当寻找的树不是连通的时候(初始时所有结点都是分隔开的),它就会为在每一颗树中搜寻出一个MST。

主要属性和方法设计

  1. 构造方法__init__()中
    UFT是一个优化后的并查集UF_Tree_Weighted对象,索引代表顶点,使用其in_the_same_group(v, w)可以判断两个顶点是否在同一颗树上,使用其unite()方法可以将两个顶点所在的树合并
    all_edges 是一个最小优先队列MinPriorityQueue的对象,储存图中所有的边,并使用最小优先队列进行按照权重对边进行排序
    edges_MST 是一个列表(当做队列),储存了MST的所有边,get_all_edges_mst()返回的就是它,可以直观地观察MST的边
  2. 生成MST的方法直接在构造方法中实现,调用即可完成

并查集传送、
最小优先队列传送(最小优先队列需要小小地修改比较的方法,应该比较传入的Edge对象的weight,否则直接比较会报错):

    def less(self, i, j):return operator.lt(self.heap[i].weight, self.heap[j].weight)

优化后的并查集传送
最小优先队列传送
加权无向边
加权无向图

Kruskal算法Python代码寻找MST

from Structure.UF.UF_Tree_Weighted import UF_Tree_Weighted
from Structure.PriorityQueue.MinPriorityQueue import MinPriorityQueue
from Structure.graph.WeightedUndigraph import WeightedUndigraph
from Structure.graph.Edge import Edgeclass KruskalMST:def __init__(self, graph):self.graph = graphself.N = self.graph.num_verticesself.UFT = UF_Tree_Weighted(self.N)self.all_edges = MinPriorityQueue()self.edges_MST = []for e in self.graph.get_all_edges():self.all_edges.append(e)while not self.all_edges.is_empty() and len(self.edges_MST) < self.N - 1:min_edge = self.all_edges.extract_min()v = min_edge.either()w = min_edge.opposite(v)if self.UFT.in_the_same_group(v, w):continueself.UFT.unite(v, w)self.edges_MST.append(min_edge)def get_all_edges_mst(self):return self.edges_MSTif __name__ == '__main__':with open('../MST.txt', 'r') as f:num_vertices = int(f.readline())num_edges = int(f.readline())graph = WeightedUndigraph(num_vertices)for e in range(num_edges):v1, v2, w = f.readline().split()graph.add_edge(Edge(int(v1), int(v2), float(w)))K_MST = KruskalMST(graph)for e in K_MST.get_all_edges_mst():v = e.either()w = e.opposite(v)weight = e.weightprint(f"v: {v} w: {w} weight: {weight}")

运行结果:

v: 0 w: 7 weight: 0.16
v: 2 w: 3 weight: 0.17
v: 1 w: 7 weight: 0.19
v: 0 w: 2 weight: 0.26
v: 5 w: 7 weight: 0.28
v: 4 w: 5 weight: 0.35
v: 6 w: 2 weight: 0.4

MST.txt

8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469478.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像分类_04神经网络最优化过程:反向传播+代码实现

logistic模型原理与推导过程分析&#xff08;1&#xff09;https://blog.csdn.net/qq_39237205/article/details/121031296https://blog.csdn.net/qq_39237205/article/details/121031296 logistic模型原理与推导过程分析&#xff08;2&#xff09;https://blog.csdn.net/qq_3…

数据结构之图:加权有向图与dijkstra算法找到最短路径,Python——28

加权有向图与dijkstra算法找到最短路径 加权有向图的构造 最短路径问题与最短路径树 最短路径问题&#xff08;The shortest path problem&#xff09;定义 最短路径可以是时间花费最短&#xff0c;也可以是距离最短&#xff0c;或是花费最少在图论中&#xff0c;最短路径问…

李沐动手学深度学习pytorch :问题:找不到d2l包,No module named ‘d2l’

同学你好&#xff01;本文章于2021年末编写&#xff0c;已与实际存在较大的偏差&#xff01; 故在2022年末对本系列进行填充与更新&#xff0c;欢迎大家订阅最新的专栏&#xff0c;获取基于Pytorch1.10版本的理论代码(2023版)实现&#xff0c; Pytorch深度学习理论篇(2023版)…

解决loaded more than 1 DLL from .libs和No metadata found in lib\site-packages两个错误

### 卸载numpy pip uninstall numpy 解决No metadata found in lib\site-packages 去这个文件夹下找到numpy的两个文件夹 删除 然后重新输入pip install numpy

ERROR 2002 (HY000): Can‘t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock‘

启动数据库时报错&#xff0c;代码ERROR 2002 (HY000) 报错截图&#xff1a; 原因&#xff1a;网络环境发生改变&#xff0c;mysql配置文件中绑定的IP与现在系统的IP不一致 解决&#xff1a;修改配置文件中绑定的IP地址为本系统的IP地址 使用命令&#xff1a; sudo vi /etc/…

图像目标分割_1 概述

6.1.1 什么是目标分割 定义&#xff1a;在计算机视觉领域&#xff0c;图像分割&#xff08;Object Segmentation&#xff09;指的是将数字图像细分为多个图像子区域&#xff08;像素的集合&#xff09;的过程。 图像分割的目的&#xff1a;简化或改变图像的表示形式&#xff0…

字符数组和strcpy

已知strcpy函数的原型是char *strcpy(char *strDest, const char *strSrc);&#xff0c;其中strDest是目的字符串&#xff0c;strSrc是源字符串。 &#xff08;1&#xff09;Write the function strcpy, dont call C/C string library.&#xff08;不调用C/C的字符串库函数&…

Django开发中问题和报错集合

记录django项目开发过程中的遇到的问题&#xff0c;导致原因和已经奏效的解决方法 常见报错UnicodeDecodeError: ‘utf-8’ codec can’t decode byte 0xbc in position 852: invalid start byte 这个问题在一些电脑上做django开发时经常出现 要么是py文件运行是就报这个类似…

图像目标分割_2 FCN(Fully Convolutional Networks for Semantic Segmentation)

6.2.1 FCN 背景介绍 图像语义分割&#xff1a;给定一张图片&#xff0c;对图片上每一个像素点进行分类&#xff01;但是与图像分类目的不同&#xff0c;语义分割模型要具有像素级的密集预测能力才可以。 6.2.2 FCN介绍 6.2.2.1 全卷积网络 全卷积网络&#xff0c;模型由卷积…

求4个数字组成的不重复三位数,Python简洁解法

求4个数字组成的不重复三位数&#xff0c;Python解法 题目要求&#xff1a; 求所有由5,6,7,8组成的数字不重复的三位数 不重复的三位数&#xff0c;即不能出现555,566这种 通过分析&#xff0c;可以使用画树的方法来确定要求的三位数的值与个数 按照上图所示可以确定由5&am…

图像目标分割_3 SegNet + U-Net

6.3.1 SegNet背景 SegNet的主要动机是场景理解的应用。 难点&#xff1a;因此它在设计的时候考虑了要在预测期间保证内存和计算时间上的效率。分割的任务其实应用非常地广&#xff0c;需要理解各个像素之间的关系&#xff0c;比如要区分出人行道和车行道&#xff0c;建筑物和…

MIPI屏数据发送命令解析

MIPI数组发送那里有一个数组&#xff0c;这个数组包含寄存器和寄存器的值&#xff1a; 相当于: 0XC480寄存器下参数0X9C&#xff1b; 0XFF00寄存器下参数0XFF,0XFF,0XFF&#xff1b; 0XC0B5 尝试一下寄存器 0x08 0x18 0x48 0x58 试一下能不能旋转

面向对象之类的内建函数

类的特殊成员 上文介绍了Python的类成员以及成员修饰符&#xff0c;从而了解到类中有字段、方法和属性三大类成员&#xff0c;并且成员名前如果有两个下划线&#xff0c;则表示该成员是私有成员&#xff0c;私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况&…

图像目标分割_4 DeepLab-V1

6.4.1 DeepLab 背景 相比于传统的视觉算法(SIFT或HOG)&#xff0c;Deep-CNN以其end-to-end方式获得了很好的效果。这样的成功部分可以归功于Deep-CNN对图像转换的平移不变性(invariance)&#xff0c;这根本是源于重复的池化和下采样组合层。平移不变性增强了对数据分层抽象的能…

图像目标分割_5 DeepLab V2 V3 V3+

6.5.1 DeepLab V2 6.5.1.1 改变特点 atrous convolution采用ASPP ( atrous spatial pyramid pooling) 多尺度获得更好的分割效果合并深度卷积网络和概率图模型方法&#xff0c;增强对物体边界的定位。基础层由VGG16转为ResNet 和v1不同&#xff1a; 通过多尺度输入处理或者多…

Python导包、模块报错的问题

import报错No module named "xxx"的问题 如何将指定目录作为项目根目录&#xff0c;让项目根目录下的包/模块都可以直接导入&#xff1f;&#xff08;linux下&#xff09; Python导入模块时&#xff0c;解释器如何定位模块&#xff1a; 1.当前目录 2.内置模块列表 3…

CC2540 串口0 通道2配置

从图里面可以看出来&#xff0c;串口0有两个通道&#xff0c;一个通道是P02 P03两个GPIO口。 还有一个通道是P14 P15两个GPIO口。 在软件配置的时候&#xff0c;主要是配置的是一个通道相关的寄存器。 7.6.4 USART 0 The SFR register bit PERCFG.U0CFG selects whether to u…

图像目标分割_6 Mask RCNN

6.6.0 背景 目标检测和语义分割的效果在短时间内得到了很大的改善。在很大程度上&#xff0c;这些进步是由强大的基线系统驱动的&#xff0c;例如&#xff0c;分别用于目标检测和语义分割的Fast/Faster R-CNN和全卷积网络(FCN)框架。这些方法在概念上是直观的&#xff0c;提供…

SCI论文写作训练营笔记汇总01_概述+文献检索与管理

1 概述 1.1 适用人群 ①初涉科研&#xff0c; 目前或将来有英文科技论文发表需求的科研工作者 ②正在撰写或准备撰写英文科技论文的科研工作者 1.2 科技论文的基本结构 1.3 科技论文组成部分的写作方法 1.4 阅读文献的重要性 2、文献检索与管理 2.1 如何查找文献参考 2.2 文…

天猫11.11:搜索引擎实时秒级更新(转载)

搜索是很多用户在天猫购物时的第一入口&#xff0c;搜索结果会根据销量、库存、人气对商品进行排序&#xff0c;而商品的显示顺序往往会决定用户的选择&#xff0c;所以保证搜索结果的实时性和准确性非常重要。在电商系统中&#xff0c;特别是在“双十一”这样的高并发场景下&a…