
Шелест ЛР / ПиАГМ2
.docxГУАП
КАФЕДРА № 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()