计算机科学与工程学院实验报告
课程名称 | 算法设计与分析 | 班级 | ||||||
实验内容 | 实验2:图的同构识别 | 指导教师 | ||||||
姓名 | 重剑DS | 学号 | 实验日期 | 2022.05.19 |
一、问题描述,含输入、输出数据内容、格式
前言:
在Wikipedia(维基百科)上是这么描述同构的:同构是在数学对象之间定义的一类映射,它能揭示出在这些对象的属性或者操作之间存在的关系。若这两个数学结构之间存在同构映射,那么这两个结构叫做是同构的。一般来说,如果忽略掉同构的对象的属性或操作的具体定义,单从结构上讲,同构的对象是完全等价的。
使用图论中的术语来描述的话:假设G=(V,E)和G1=(V1,E1)是两个图,如果存在一个双射m:V→V1,使得对所有的x,y∈V均有xy∈E等价于m(x)m(y)∈E1,则称G和G1是同构的。这样的一个映射m称之为一个同构,如果G=G1,则称他为一个自同构(isomorphism)。在同构意义下封闭的图族叫做图性质。[1]
同构图形,是指将两个或两个以上的图形通过图形设计的组合、嫁接等处理手段组合在一起,共同构成一个新图形,并且要传达出一个新的意义。这个新图形并不是原图形的简单相加,而是一种图形意义的超越或突变,从而形成强烈的视觉冲击力。[2]
问题描述:
图的同构识别问题。给定两个无向图的顶点和顶点间的联系,判断两个图是否同构,如果是输出“Yes”,否则输出“No”。
输入数据格式:
第1行有2个正整数n,m,表示给定的图G1有n个顶点以及G2有m个结点。顶点编号为1,2,…,n。接下来分别给出图G1的n×n的邻接矩阵以及图G2的m×m的邻接矩阵。
【输入样例】
6 6
0 1 0 0 0 0
1 0 1 0 0 0
0 1 0 1 0 0
0 0 1 0 1 1
0 0 0 1 0 0
0 0 0 1 0 0
0 1 0 0 0 0
1 0 1 0 0 1
0 1 0 1 0 0
0 0 1 0 1 0
0 0 0 1 0 0
0 1 0 0 0 0
输出数据格式:
程序运行结束时,将计算出的结果输出。两图同构则输出”Yes”,不同构则输出”No”。
【输出样例】
Yes
二、调试环境介绍
IDE:Dev-C++ 5.4.0
编程语言:C++
调试数据及结果(包含指定的调试数据及结果),有调试界面截图
调试的输入数据:
6 6
0 1 0 0 0 0
1 0 1 0 0 0
0 1 0 1 0 0
0 0 1 0 1 1
0 0 0 1 0 0
0 0 0 1 0 0
0 1 0 0 0 0
1 0 1 0 0 1
0 1 0 1 0 0
0 0 1 0 1 0
0 0 0 1 0 0
0 1 0 0 0 0
使用python,借助matplotlib画出对应上面输入的邻接矩阵的图1和图2,如下图所示:
图1
图2
由上面可视化后的图像不难看出两图是同构的。
上机输入效果界面截图:
运行结果:
最终运行界面截图:
中间运行过程debug调试截图:
由上图可以看到,f为true,说明此时的排列所构成的temp邻接矩阵和图2的邻接矩阵并不相等
四、算法流程图、算法的时间复杂度和空间复杂度
算法流程图如下图所示:
算法的时间复杂度如下:
根据图同构的必要非充分条件:
- 结点数目相同 ②边数相同 ③度数相同的结点数相同。
在本代码中,最好情况下时间复杂度:当两图结点个数不同时,直接输出两图非同构,
所以最好情况时间复杂度为O(1)。
平均或最坏时间复杂度:时间复杂度主要来源于全排列的代码,产生图1结点的全部排列序列的量级是n!。在n!循环内又有两次n2的for循环,故时间复杂度为O(n2×n!)。
算法空间复杂度分析如下:
算法的空间复杂度主要来自用于存图的邻接矩阵
。
其大小和问题所提供的图的节点数n和m有关,所以空间复杂度S(n)=O(max(n2, m2))。
- 实验中遇到的问题及解决方案(10-50字)
在实验过程中,使用vector动态生成二维数组时,奇怪为什么用上面的输入样例程序会崩溃,如下图所示
后面发现是数组大小开小了。 错误如下图所示:
因为结点序号是从1开始的,然而数组下标是从0开始,故需要数组需要多开一维即可。改成之后如下图所示:
六、参考文献(2-5个)
[1] Reinhard Diestel.图论:施普林格出版社,中文版为高等教育出版社,2013.1
[2] 潘杨,谷莉,韩君主编.图形创意:北京工业大学出版社,2012.08
附:详细代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10;
int n, m;
int a[N];
int main()
{
cin >> n >> m; //输入图1、2的结点数
if (n != m) {
cout << "No";
return 0;
}
//根据结点数n、m,动态生成两个二维数组来存图(使用邻接矩阵的方式)
vector<int> w1(n+1, 0);
vector<vector<int> > g1(n+1, w1);
vector<int> w2(m+1, 0);
vector<vector<int> > g2(m+1, w2);
for (int i = 1; i <= n; i ++ ) a[i] = i; //初始化a数组
for (int i = 1; i <= n; i++) //输入图1的邻接矩阵
for (int j = 1; j <= n; j++)
cin >> g1[i][j];
for (int i = 1; i <= m; i++) //输入图2的邻接矩阵
for (int j = 1; j <= m; j++)
cin >> g2[i][j];
//全排列
do {
bool f = 0;
int temp[N][N];
memset(temp, 0, sizeof temp);
for (int i = 1; i <= n; i ++)
{
for (int j = 1; j <= n; j ++ )
{
temp[i][j] = g1[a[i]][a[j]];
}
}
for (int i = 1; i <= n; i ++ )
{
for (int j = 1; j <= n; j ++ )
{
if (temp[i][j] != g2[i][j] )
{
f = 1;
break;
}
}
if (f == 1) break;
}
if (f == 0)
{
cout << "Yes" << endl;
return 0;
}
} while (next_permutation(a + 1, a + 1 + n));
cout << "No" << endl;
return 0;
}
/*
6 6
0 1 0 0 0 0
1 0 1 0 0 0
0 1 0 1 0 0
0 0 1 0 1 1
0 0 0 1 0 0
0 0 0 1 0 0
0 1 0 0 0 0
1 0 1 0 0 1
0 1 0 1 0 0
0 0 1 0 1 0
0 0 0 1 0 0
0 1 0 0 0 0
*/
画图用的python代码
import networkx as nx
import matplotlib.pyplot as plt
def adMmatrix2Img(matrix):
G = nx.Graph()
n = len(matrix)
point = []
for i in range(n):
point.append(i)
G.add_nodes_from(point)
edglist = []
for i in range(n):
for k in range(i + 1, n):
if matrix[i][k] > 0:
edglist.append((i, k))
G.add_edges_from(edglist)
position = nx.circular_layout(G)
nx.draw_networkx_nodes(G, position, nodelist=point, node_color="y")
nx.draw_networkx_edges(G, position)
nx.draw_networkx_labels(G, position)
plt.show()
if __name__ == '__main__':
# matrix = [[0,1,0,0,0,0],
# [1,0,1,0,0,0],
# [0,1,0,1,0,0],
# [0,0,1,0,1,1],
# [0,0,0,1,0,0],
# [0,0,0,1,0,0]
# ]
matrix = [[0, 1, 0, 0, 0, 0],
[1, 0, 1, 0, 0, 1],
[0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 0],
[0, 0, 0, 1, 0, 0],
[0, 1, 0, 0, 0, 0]
]
adMmatrix2Img(matrix)