图的连通性问题

一个图中有10个顶点,建立一个图,使得图中任意4个点离开网络该图仍然是连通图

问题描述

问题描述:一个图中有10个顶点,建立一个图,使得图中任意4个点离开网络该图仍然是连通图

解决思路

不妨假设图中存在某个结点 X 仅有4个结点与之相连,那么只要这四个点离开,点X就会成为孤立点,图的连通性必然被破坏。因此,我们得出结论,每个点与其他5个点直接相连是这一问题的必要条件,但是未必充分。事实上,读者可以尝试列举反例,以发现这一条件是不充分的。

为此,决定以“每个点与其他5个点相连”为起点进行求解这样的图结构,这样可以保证图中边的数量尽可能少。我们使用邻接矩阵对该图进行描述。

初始化

初始情况下,我们们的邻接矩阵是这样的,保证图中有且仅有25条边。当然,其实不一定非得是下图中的样子,只要保证矩阵是包含50个1的对称矩阵即可。

 [0 1 1 1 1 1 1 1 0 0]
 [1 0 1 1 1 1 1 1 0 0]
 [1 1 0 1 1 1 1 1 0 0]
 [1 1 1 0 1 1 1 1 0 0]
 [1 1 1 1 0 1 1 0 0 0]
 [1 1 1 1 1 0 1 0 0 0]
 [1 1 1 1 1 1 0 0 0 0]
 [1 1 1 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]

矩阵处理

对矩阵进行处理的目的是使得矩阵中每行每列均包含5个1,即图中每个结点有且仅有5个其他点与之直接相连。这一变化的详细思路可以参见下面代码中的 move_edge 方法。

如何保证去掉任意4个结点后图是连通的

在经过上一个过程后,每个结点都满足了与另外5个结点相连的条件。
我们用 [A] 表示与结点A直接相连的结点。
我们可以考虑这样的情形:

  • 如果存在结点 A 和 B, 如果 [A] ∩ [B] 包括4个结点,且[A]∪[B] - [A] ∩ [B] = {A, B}, 那么这样一个图一定是不满足条件的。
  • 如果存在结点 A B C, 如果 [A] ∩ [B] ∩ [C] 包括3个或4个结点,且[A]∪[B]∪[C] - [A] ∩ [B] ∩ [C] = {A, B, C}, 那么这个图一定是不满足条件的。
    事实上,这是10个点去掉4个点的情况下存在的情形。如果点的总数增加,问题会变得更复杂。

下面是完整的 python 算法。

其运行结果:
这里写图片描述

源程序

#!/usr/bin/python
# -*- coding: utf-8 -*-

import copy, random
import numpy as np
from colorama import init, Fore, Back, Style

'''
Problem:
    一个图中有10个顶点,建立一个图,使得图中任意4个点离开网络该图仍然是连通图
'''

class AdjacencyMatrix(object):
    '''Node number is 10 and the matrix is 10 * 10'''
    node_num = 10
    matrix = np.uint8(np.zeros((node_num, node_num)))

    def __init__(self):
        '''initial the matrix with 0 and 1, while 1 means connection and 0 means no connection'''
        init(autoreset=True)
        edge_num = self.node_num * 5 / 2
        for i in range(0, self.node_num):
            for j in range(0, i):
                if i*(i-1)/2+j+1 <= edge_num:
                    self.matrix[i][j] = self.matrix[j][i] = 1
        print self.matrix

    def node_connected(self, node):
        '''get all nodes collected to the given node'''
        node_set = set([])
        try:
            for i in range(0, self.node_num):
                if self.is_node_connect(node, i):
                    node_set.add(i)
        except:
            self.printError('fail in node_connected')
        return node_set

    def over_connected(self):
        '''if a node has more than 5 edge, we think it is over connected'''
        node_set = set([])
        for i in range(0, self.node_num):
            if sum(self.matrix[i]) > 5:
                node_set.add(i)
        return node_set

    def under_connected(self):
        '''if a node has less than 5 edge, we think it is under connected'''
        node_set = set([])
        for i in range(0, self.node_num):
            if sum(self.matrix[i]) < 5:
                node_set.add(i)
        return node_set

    def is_node_connect(self, i, j):
        '''decide if node i and j are connected'''
        return bool(self.matrix[i][j]) or bool(self.matrix[j][i])

    def move_edge(self):
        '''try to move the edge so that every node has five edges connected to it, which means every row and collunm has five 1'''
        nodes_over_connect = sorted(list(self.over_connected()))
        nodes_under_connect = sorted(list(self.under_connected()))
        for node in nodes_under_connect:
            for j in range(0, self.node_num):
                if self.matrix[node][j] == 0:
                    for i in range(0, node):
                        if i in nodes_over_connect and self.matrix[i][j] == 1:
                            self.matrix[node][j] = self.matrix[j][node] = 1
                            self.matrix[i][j] = self.matrix[j][i] = 0
                            nodes_over_connect = sorted(list(self.over_connected()))
                            nodes_under_connect = sorted(list(self.under_connected()))
                            self.self_test()
                            break
                    if sum(self.matrix[node]) == 5:
                        break
        self.is_all_five()

    def is_subset_fail(self, set_size):
        '''test if the graph contains the failing model'''
        for i in range(0, self.node_num):
            conn_A = self.node_connected(i)
            for j in range(i+1, self.node_num):
                conn_B = self.node_connected(j)
                if conn_A ^ conn_B == set([i, j]):
                    self.printWarn('Need to be adjusted')
        self.printPass('All two nodes OK')
        for i in range(0, self.node_num):
            conn_A = self.node_connected(i)
            for j in range(i+1, self.node_num):
                conn_B = self.node_connected(j)
                for k in range(j+1, self.node_num):
                    conn_C = self.node_connected(k)                 
                    if conn_A ^ conn_B ^ conn_C == set([i, j, k]):
                        self.printWarn('Need to be adjusted')
        self.printPass('All three nodes OK')

    def is_all_five(self):
        '''test if every node is collected by 5 other nodes'''
        for i in range(0, self.node_num):
            if sum(self.matrix[i]) != 5:
                self.printError('Not all five')
                return False
        self.printPass('Adjust to all five success')
        return True


    def self_test(self):
        '''test if there is something wrong with the matrix when processing'''
        for i in range(0, self.node_num):
            for j in range(0, i+1):
                if self.matrix[i][j] != self.matrix[j][i]:
                    self.printError('Self-Test: Fail')
                    exit()
        if sum(sum(self.matrix)) != 50:
            self.printError('Self-Test: Fail')
            exit()
        self.printPass('Self-Test: Pass')

    def random_test(self, test_times):
        '''black-box test for the graph'''
        for i in range(0, test_times):
            remove_nodes = random.sample(range(0, self.node_num), 4)
            matrix_copy = copy.deepcopy(self.matrix)
            for node in remove_nodes:
                self.matrix[node] = 0
                self.matrix[:][node] = 0
            if self.is_connected_graph():
                self.printPass('The test ' + str(i) + ' success')
            else:
                self.printError('The test ' + str(i) + ' fail')
            self.matrix = matrix_copy

    def is_connected_graph(self):
        '''decide the graph is connected or not by the depth first searching algorithm'''
        travelled = []
        def dfs(node):
            travelled.append(node)
            neighbors = self.node_connected(node)
            for item in neighbors:
                if item not in travelled:
                    dfs(item)
        dfs(0)
        return set(travelled) == set(range(10))

    def printError(self, text):
        '''print error information'''
        print(Back.RED + text)
#       print(Fore.RED + 'some red text')
#       print(Back.GREEN + 'and with a green background')
#       print(Style.DIM + 'and in dim text')
#       print(Fore.RESET + Back.RESET + Style.RESET_ALL)  autoreset=True
#       print('back to normal now')

    def printWarn(self, text):
        '''print waring information'''
        print(Back.YELLOW + text)

    def printPass(self, text):
        '''print success information'''
        print(Back.GREEN + text)

if __name__ == '__main__':
    m = AdjacencyMatrix()
    m.self_test()
    m.move_edge()
    m.is_subset_fail(2)
    print m.matrix
    m.random_test(10)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值