基于矩阵实现的Connected Components算法

2024-05-12 23:48

本文主要是介绍基于矩阵实现的Connected Components算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.连通分支

连通分支(Connected Component)是指:在一个图中,某个子图的任意两点有边连接,并且该子图去剩下的任何点都没有边相连。在 Wikipedia上的定义如下:
In graph theory, a connected component (or just component) of an undirected graph is a subgraph in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in the supergraph.

如Wikipedia上给的例子:


根据定义不难看出该图有三个连通分支。
另外,连通分支又分为弱连通分支和强连通分支。强连通分支是指子图点i可以连接到点j,同时点j也可以连接到点i。弱连通(只对有向图)分支是指子图点i可以连接到点j,但是点j可以连接不到点i。

2.传统算法

2.1算法思想

在传统算法中,我们是根据一点任意点,去找它的邻结点,然后再根据邻结点,找它的邻结点,直到所有的点遍历完。如下图:

图中有A、B、C、D和E五个点。我们先任选一个点C,然后找到C的邻结点E。然后我们再找E的邻结点,发现只有C,但是我们之前遍历过C,所以排除C。现在我们没有点可以遍历了,于是找到了第一个连通分支,如下蓝色标记:


接着,我们再从没有遍历的点(A、B、C)中任选一个点A。按照上述的方法,找到A的邻结点B。然后在找B的邻结点,找到A和D,但是A我们之前遍历过,所以排除A,只剩下了D。再找D的邻结点,找到B,同样B之前也遍历过了,所以也排除B,最后发现也没有点可以访问了。于是,就找到了第二个连通分支。到此图中所有的点已经遍历完了,因此该图有两个连通分支(A、B、D)和(C、E)。如下图:


2.2算法实现

下面是用Python实现的代码:
class Data(object):def __init__(self, name):self.__name = nameself.__links = set()@propertydef name(self):return self.__name@propertydef links(self):return set(self.__links)def add_link(self, other):self.__links.add(other)other.__links.add(self)# The function to look for connected components.
def connected_components(nodes):# List of connected components found. The order is random.result = []# Make a copy of the set, so we can modify it.nodes = set(nodes)# Iterate while we still have nodes to process.while nodes:# Get a random node and remove it from the global set.n = nodes.pop()print(n)# This set will contain the next group of nodes connected to each other.group = {n}# Build a queue with this node in it.queue = [n]# Iterate the queue.# When it's empty, we finished visiting a group of connected nodes.while queue:# Consume the next item from the queue.n = queue.pop(0)# Fetch the neighbors.neighbors = n.links# Remove the neighbors we already visited.neighbors.difference_update(group)# Remove the remaining nodes from the global set.nodes.difference_update(neighbors)# Add them to the group of connected nodes.group.update(neighbors)# Add them to the queue, so we visit them in the next iterations.queue.extend(neighbors)# Add the group to the list of groups.result.append(group)# Return the list of groups.return result# The test code...
if __name__ == "__main__":# The first group, let's make a tree.a = Data("a")b = Data("b")c = Data("c")d = Data("d")e = Data("e")f = Data("f")a.add_link(b)    #      aa.add_link(c)    #     / \b.add_link(d)    #    b   cc.add_link(e)    #   /   / \c.add_link(f)    #  d   e   f# The second group, let's leave a single, isolated node.g = Data("g")# The third group, let's make a cycle.h = Data("h")i = Data("i")j = Data("j")k = Data("k")h.add_link(i)    #    h————ii.add_link(j)    #    |    |j.add_link(k)    #    |    |k.add_link(h)    #    k————j# Put all the nodes together in one big set.nodes = {a, b, c, d, e, f, g, h, i, j, k}# Find all the connected components.number = 1for components in connected_components(nodes):names = sorted(node.name for node in components)names = ", ".join(names)print("Group #%i: %s" % (number, names))number += 1# You should now see the following output:# Group #1: a, b, c, d, e, f# Group #2: g# Group #3: h, i, j, k

3.基于矩阵的实现算法

3.1理论基础

1.邻接矩阵
邻接矩阵表示顶点之间相邻关系的矩阵,同时邻接矩阵分为有向邻接矩阵和无向邻接矩阵,如:

2.回路计算
假设有邻接矩阵,则回路计算公式为:

其中,表示从点i到点j经过k条路径形成的通路总数。当然,如果表示从点i到点j没有通路(有向图)或回路路(无向图)。


3.2算法推导

如果把经过0、1、2、3••••••条路径形成的回路矩阵求并集(注:经过0条路径形成的回路矩阵即为单位矩阵I),那么该并集C可以表示所有回路的总和,用公式可以表示为:
因此, 的(i,j)有非0值,表示点i和点j是同一个强连通的集合。
由于 比较复杂,但是,我们可以用下式计算:
C等于0的位置,D对应的位置也等于0;C等于1的位置,D对应的位置大于0。此时可以用D代替C进行计算。
对于D,这个式子几乎不收敛,因为我们需要修改它。现在让我们考虑式子 ,证明如下:


因此,

但是,现在还有一个问题,D并不总是收敛。因此,需要引入一个系数 ,使得用 代替A,则D重新定义为:

同理,由(证明同上),


可以求得:


其中,矩阵D中非0值所在列相同的行,属于同一个连通分支,即行结构相同的是一个连通分支。

3.3算法实现

下面是用Python实现的代码:
from numpy.random import rand
import numpy as np#利用求并集和交集的方法
def cccomplex(adjacencyMat):def power(adjacencyMatPower, adjacencyMat):adjacencyMatPower *= adjacencyMatreturn adjacencyMatPowerdimension = np.shape(adjacencyMat)[0]eye = np.mat(np.eye(dimension))adjacencyMat = np.mat(adjacencyMat)adjacencyMatPower = adjacencyMat.copy()result = np.logical_or(eye, adjacencyMat)for i in range(dimension):adjacencyMatPower = power(adjacencyMatPower, adjacencyMat)result = np.logical_or(result, adjacencyMatPower)final = np.logical_and(result, result.T)return final#利用求矩阵逆的方法    
def connectedConponents(adjacencyMat, alpha = 0.5):n = np.shape(adjacencyMat)[0]E = np.eye(n)ccmatrix = np.mat(E - alpha * adjacencyMat)return ccmatrix.Idef init(dimension):mat = np.ones((dimension, dimension))mat[(rand(dimension) * dimension).astype(int), (rand(dimension) * dimension).astype(int)] = 0return matif __name__ == "__main__":dimension = 4adjacencyMat = init(dimension)adjacencyMat1 = np.array([[0,1,0,0,0,0,0,0],                          									                                        [0,0,1,0,1,1,0,0],   [0,0,0,1,0,0,1,0],[0,0,1,0,0,0,0,1],[1,0,0,0,0,1,0,0],[0,0,0,0,0,0,1,0],    [0,0,0,0,0,1,0,1],[0,0,0,0,0,0,0,1]])   adjacencyMat2 = np.array([[0,1,1,0],[1,0,0,0],[1,0,0,0],[0,0,0,0]])print(cccomplex(adjacencyMat1))                 #(A, B, E) (C, D) (F, G) (H)print(connectedConponents(adjacencyMat2))   #[[ True  True False False  True False False False]# [ True  True False False  True False False False]# [False False  True  True False False False False]# [False False  True  True False False False False]# [ True  True False False  True False False False]# [False False False False False  True  True False]# [False False False False False  True  True False]# [False False False False False False False  True]]#[[ 2.   1.   1.   0. ]# [ 1.   1.5  0.5  0. ]# [ 1.   0.5  1.5  0. ]# [ 0.   0.   0.   1. ]]

4.评价

基于矩阵运算实现的图算法,有以下几点优势:
  1. 易于实现。利用矩阵可以仅仅通过矩阵与矩阵或矩阵与向量的运算就可以实现一些图算法。
  2. 易于并行。在超大矩阵运算中,可以采用分块的方式平行处理矩阵运算。同理,也可以很容易的移植分布式上(可以参考我的博文基于Spark实现的超大矩阵运算)。
  3. 效率更高。基于矩阵的图形算法更清晰突出的数据访问模式,可以很容易地优化(实验测试工作已完成)。
  4. 易于理解。这个需要对离散数学或者图论有一定基础,知道每一步矩阵运算所对应的物理意义或数学意义。



这篇关于基于矩阵实现的Connected Components算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/984082

相关文章

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1