Скачиваний:
6
Добавлен:
03.06.2024
Размер:
180.41 Кб
Скачать

ГУАП

КАФЕДРА № 41

ОТЧЕТ ЗАЩИЩЕН С ОЦЕНКОЙ

ПРЕПОДАВАТЕЛЬ

старший преподаватель

М.Н. Шелест

должность, уч. степень, звание

подпись, дата

инициалы, фамилия

ОТЧЕТ О ЛАБОРАТОРНОЙ РАБОТЕ №2

БАЗОВЫЕ АЛГОРИТМЫ НА ГРАФАХ

по курсу: ПОСТРОЕНИЕ И АНАЛИЗ ГРАФОВЫХ МОДЕЛЕЙ

РАБОТУ ВЫПОЛНИЛ

СТУДЕНТ ГР. №

подпись, дата

инициалы, фамилия

Санкт-Петербург 2024

Цель работы: реализовать и проверить на тестовом примере базовый алгоритм на графе. Сравнить быстродействие реализованного алгоритма на специальных графах.

Вариант № 15

На рисунках 1, 2 представлены алгоритм и тип графа согласно варианту.

Рисунок 1 – Алгоритм

Рисунок 2 – Тип графа

Ход работы

Составлен в представлении списка ребер и визуализирован граф (Рисунок 3).

Рисунок 3 - Исходный граф

Листинг 1 – Создание исходного графа

import networkx as nx

import numpy as np

# Создание исходного графа

G = nx.Graph()

# Добавление ребер в граф

G.add_edges_from([

('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'A'),

('C', 'E'), ('E', 'F'), ('E', 'G')

])

# Функция для визуализации графа

def draw_graph(G, title):

pos = nx.kamada_kawai_layout(G)

nx.draw(G, pos=pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500)

plt.title(title)

plt.show()

# Визуализация исходного графа

draw_graph(G, "Исходный граф")

Создана и выполнена функция для выполнения гомеоморфного сжатия графа (Рисунок 4).

Алгоритм гомеоморфного сжатия графов используется для упрощения графа путём удаления вершин степени 2 и объединения инцидентных им рёбер. Этот процесс позволяет получить более простую структуру, сохраняя при этом топологические свойства исходного графа.

Шаги алгоритма:

1. Поиск всех вершин степени 2 графа. Степень вершины – это количество рёбер, инцидентных этой вершине.

2. Для каждой найденной вершины степени 2 удаление этой вершины из графа, а два инцидентных ей ребра заменяются одним ребром, которое напрямую соединяет две соседние вершины, ранее связанные через удаляемую вершину.

3. Повторение шагов 1 и 2 до тех пор, пока в графе не останется вершин степени 2.

Результатом такого алгоритма будет граф, в котором нет вершин степени 2, и который является гомеоморфным исходному.

Рисунок 4 – Результат сжатия графа

Сначала была удалена вершина A, добавлено ребро BD, затем удалена вершина B, ребро CD уже существует, удалена вершина C, добавлено ребро ED.

Листинг 2 – Алгоритм гомеоморфного сжатия графа

def homeomorphic_compression(G):

# Создание копии графа, чтобы не изменять исходный граф

compressed = G.copy()

# Флаг изменений в графе

changed = True

while changed:

changed = False

# Перебор всех вершин в графе

for node in list(compressed.nodes):

# Проверка, является ли степень вершины равной 2

if compressed.degree(node) == 2:

# Получение соседей вершины

neighbors = list(compressed.neighbors(node))

neighbor1, neighbor2 = neighbors[0], neighbors[1]

# Добавление ребра между соседями, если его нет

if not compressed.has_edge(neighbor1, neighbor2):

compressed.add_edge(neighbor1, neighbor2)

# Удаление вершины из графа

compressed.remove_node(node)

# Фиксация изменения в графе

changed = True

break # Начало цикла заново, так как граф был изменен во время итерации

# Возврат сжатого графа

return compressed

Написана функция для создания графа правильной шестиугольной решетки, граф визуализирован выведена матрица смежности графа (Рисунок 5, 6).

Рисунок 5 – Граф

Рисунок 6 – Матрица смежности графа

Листинг 3 – Представление графа в виде правильной шестиугольной решетки

# Функция для создания матрицы смежности графа правильной шестиугольной решетки

def create_adjacency_matrix(M):

# Создание нулевой квадратной матрицы Matrix размера M^2 на M^2

Matrix = np.zeros((M**2, M**2))

# Создание ребер между вершинами

for V in range(M**2):

R = V // M # Вычисление номера строки

C = V % M # Вычисление номера столбца

# Если вершина удовлетворяет условиям для создания ребер

if (R % 4 == 0 and C % 2 == 1) or (R % 4 == 2 and C % 2 == 0):

# Добавление ребер с верхними и нижними вершинами

if R > 0:

Matrix[V][V - M] = Matrix[V - M][V] = 1 # Ребро с верхней вершиной

if R < (M - 1) and C < (M - 1):

Matrix[V][V + M + 1] = Matrix[V + M + 1][V] = 1 # Ребро с нижней правой вершиной

if R < (M - 1) and C > 0:

Matrix[V][V + M - 1] = Matrix[V + M - 1][V] = 1 # Ребро с нижней левой вершиной

# Удаление лишних вершин

row_sums = np.sum(Matrix, axis=1) # Вычисление суммы по строкам

while np.any(row_sums < 2):

index = np.argmin(row_sums) # Индекс строки с минимальной суммой

Matrix = np.delete(Matrix, index, axis=0) # Удаление строки

Matrix = np.delete(Matrix, index, axis=1) # Удаление столбца

row_sums = np.sum(Matrix, axis=1) # Обновление суммы по строкам

return Matrix

# Задание значения M

M = 6

# Получение матрицы смежности

matrix = create_adjacency_matrix(M)

# Создание графа

G = nx.Graph(matrix)

# Визуализация графа

draw_graph(G, "")

# Вывод Матрицы смежности

print("Матрица смежности графа")

print(matrix)

Для возрастающего количества узлов в решетке проведены замеры среднего времени выполнения сжатия графа с 1000 повторений и построен график (Рисунок 7).

Рисунок 7 – График зависимости времени выполнения сжатия от количества узлов в графе

График показывает постоянное увеличение времени выполнения, что говорит о том, что с увеличением количества узлов время выполнения операции увеличивается. Количество узлов возрастает неравномерно, что связанно с особенностями построения правильной шестиугольной решетки, увеличение размера изначальной матрицы не всегда ведет к добавлению новых узлов.

Листинг 4 – Вычисление среднего времени выполнения и построение графика

import time

# Функция для измерения времени выполнения сжатия графа

def measure_execution_time(graph, repetitions):

# Обнуление общего времени выполнения

total_time = 0

# Цикл по числу повторений

for _ in range(repetitions):

# Начало отсчета времени

start_time = time.time()

# Сжимаем граф

compressed_graph = homeomorphic_compression(graph)

# Конец отсчета времени

end_time = time.time()

# Увеличение общего времени выполнения

total_time += end_time - start_time

# Вычисление среднего времени выполнения

avg_time = total_time / repetitions

# Возврат среднего вреени выполнения

return avg_time

# Диапазон значений M для построения решетки M^2 на M^2

M_values = range(4, 10, 1)

# Количество повторов

repetitions = 1000

# Пустые массивы для хранения количества узлов и среднего времени выполнения

n_nodes = []

execution_times=[]

# Цикл для измерения среднего времени выполнения сжатия для каждого значения M

for M in M_values:

# Создание правильной шестиугольной решетки

graph = nx.Graph(create_adjacency_matrix(M))

# Вычисление и занесение в массив числа узлов

n_nodes.append(graph.number_of_nodes())

# Вычисление и занесение в массив среднего времени выполнения

execution_times.append(measure_execution_time(graph, repetitions))

# Построение графика

plt.figure(figsize=(10, 6))

plt.plot(n_nodes, execution_times, marker='o')

plt.xticks(n_nodes)

plt.yticks(execution_times)

plt.title('Зависимость времени выполнения от количества узлов')

plt.xlabel('Количество узлов')

plt.ylabel('Время выполнения (секунды)')

plt.grid(True)

plt.show()

Вывод: в ходе выполнения лабораторной работы были написаны подпрограммы для реализации графа правильной шестиугольной решетки и алгоритма гомеоморфного сжатия графа. Проведены замеры быстродействия алгоритма сжатия и построен график зависимости среднего времени выполнения сжатия от количества узлов в графе. График показал тенденцию возрастания времени выполнения. Алгоритм гомеоморфного сжатия может использоваться для решения реальных задач, например анализ социальных сетей: гомеоморфное сжатие может использоваться для упрощения графиков социальных сетей, что облегчает анализ социальных связей и выявление групп пользователей. Сжатый граф может помочь в выявлении ключевых лиц в сети или в выявлении общин и кластеров. Также в биоинформатике гомеоморфные графы используются для анализа и сравнения геномов. Они позволяют исследовать эволюционные связи между различными организмами и определять общие структурные элементы в геномах.

Листинг 5 – Полный код программы

import networkx as nx

import matplotlib.pyplot as plt

import numpy as np

import time

# Функция для создания матрицы смежности графа правильной шестиугольной решетки

def create_adjacency_matrix(M):

# Создание нулевой квадратной матрицы Matrix размера M^2 на M^2

Matrix = np.zeros((M**2, M**2))

# Создание ребер между вершинами

for V in range(M**2):

R = V // M # Вычисление номера строки

C = V % M # Вычисление номера столбца

# Если вершина удовлетворяет условиям для создания ребер

if (R % 4 == 0 and C % 2 == 1) or (R % 4 == 2 and C % 2 == 0):

# Добавление ребер с верхними и нижними вершинами

if R > 0:

Matrix[V][V - M] = Matrix[V - M][V] = 1 # Ребро с верхней вершиной

if R < (M - 1) and C < (M - 1):

Matrix[V][V + M + 1] = Matrix[V + M + 1][V] = 1 # Ребро с нижней правой вершиной

if R < (M - 1) and C > 0:

Matrix[V][V + M - 1] = Matrix[V + M - 1][V] = 1 # Ребро с нижней левой вершиной

# Удаление лишних вершин

row_sums = np.sum(Matrix, axis=1) # Вычисление суммы по строкам

while np.any(row_sums < 2):

index = np.argmin(row_sums) # Индекс строки с минимальной суммой

Matrix = np.delete(Matrix, index, axis=0) # Удаление строки

Matrix = np.delete(Matrix, index, axis=1) # Удаление столбца

row_sums = np.sum(Matrix, axis=1) # Обновление суммы по строкам

return Matrix

# Функция гомеоморфного сжатия

def homeomorphic_compression(G):

# Создание копии графа, чтобы не изменять исходный граф

compressed = G.copy()

# Флаг изменений в графе

changed = True

while changed:

changed = False

# Перебор всех вершин в графе

for node in list(compressed.nodes):

# Проверка, является ли степень вершины равной 2

if compressed.degree(node) == 2:

# Получение соседей вершины

neighbors = list(compressed.neighbors(node))

neighbor1, neighbor2 = neighbors[0], neighbors[1]

# Добавление ребро между соседями, если его нет

if not compressed.has_edge(neighbor1, neighbor2):

compressed.add_edge(neighbor1, neighbor2)

# Удаление вершины из графа

compressed.remove_node(node)

# Фиксация изменения в графе

changed = True

break # Начало цикла заново, так как граф был изменен во время итерации

# Возврат сжатого графа

return compressed

# Функция для визуализации графа

def draw_graph(G, title):

pos = nx.kamada_kawai_layout(G)

nx.draw(G, pos=pos, with_labels=True, node_color='lightblue', edge_color='gray', node_size=500)

plt.title(title)

plt.show()

# Создание исходного графа

G = nx.Graph()

# Добавление ребер в граф

G.add_edges_from([

('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'A'),

('C', 'E'), ('E', 'F'), ('E', 'G')

])

# Выполнение гомеоморфного сжатия

compressed_G = homeomorphic_compression(G)

# Визуализация исходного и сжатого графов

draw_graph(G, "Исходный граф")

draw_graph(compressed_G, "Сжатый граф")

# Задание значения M

M = 6

# Получение матрицы смежности

matrix = create_adjacency_matrix(M)

# Создание графа

G = nx.Graph(matrix)

# Визуализация графа и вывод матрицы

draw_graph(G, "")

print("Матрица смежности графа")

print(matrix)

# Функция для измерения времени выполнения сжатия графа

def measure_execution_time(graph, repetitions):

# Обнуление общего времени выполнения

total_time = 0

# Цикл по числу повторений

for _ in range(repetitions):

# Начало отсчета времени

start_time = time.time()

# Сжимаем граф

compressed_graph = homeomorphic_compression(graph)

# Конец отсчета времени

end_time = time.time()

# Увеличение общего времени выполнения

total_time += end_time - start_time

# Вычисление среднего времени выполнения

avg_time = total_time / repetitions

# Возврат среднего вреени выполнения

return avg_time

# Диапазон значений M для построения решетки M^2 на M^2

M_values = range(4, 10, 1)

# Количество повторов

repetitions = 1000

# Пустые массивы для хранения количества узлов и среднего времени выполнения

n_nodes = []

execution_times=[]

# Цикл для измерения среднего времени выполнения сжатия для каждого значения M

for M in M_values:

# Создание правильной шестиугольной решетки

graph = nx.Graph(create_adjacency_matrix(M))

# Вычисление и занесение в массив числа узлов

n_nodes.append(graph.number_of_nodes())

# Вычисление и занесение в массив среднего времени выполнения

execution_times.append(measure_execution_time(graph, repetitions))

# Построение графика

plt.figure(figsize=(10, 6))

plt.plot(n_nodes, execution_times, marker='o')

plt.xticks(n_nodes)

plt.yticks(execution_times)

plt.title('Зависимость времени выполнения от количества узлов')

plt.xlabel('Количество узлов')

plt.ylabel('Время выполнения (секунды)')

plt.grid(True)

plt.show()

Соседние файлы в папке Шелест ЛР