Скачиваний:
7
Добавлен:
04.06.2024
Размер:
260.14 Кб
Скачать

ГУАП

КАФЕДРА № 41

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

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

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

М.Н. Шелест

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

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

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

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

ПРЕДСТАВЛЕНИЕ ГРАФОВ В ЭВМ

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

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

СТУДЕНТ ГР. №

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

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

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

Цель работы: Изучение представления графов в ЭВМ при помощи матрицы смежности, множества пар вершин и массива структур. Визуализация графов.

Индивидуальный вариант:

Вариант №1 (21)

На рисунке 1 представлен граф

Рисунок 1- Граф по варианту

В таблице 1 представлена матрица смежности графа Таблица 1

A

B

C

D

E

F

G

A

0

1

0

3

0

0

4

B

0

0

3

0

0

2

0

C

0

0

0

2

0

3

0

D

0

0

0

0

3

0

0

E

0

0

0

0

0

0

2

F

0

0

0

0

0

0

0

G

0

0

0

0

0

0

0

Матрица смежности введена в ЭВМ в явном виде (Листинг 1)

Листинг. Введение матрицы смежности в явном виде

peaks = ['A', 'B', 'C', 'D', 'E', 'F', 'G']

matrix = [

[0, 1, 0, 3, 0, 0, 4],

[0, 0, 3, 0, 0, 2, 0],

[0, 0, 0, 2, 0, 3, 0],

[0, 0, 0, 0, 3, 0, 0],

[0, 0, 0, 0, 0, 0, 2],

[0, 0, 0, 0, 0, 0, 0],

[0, 0, 0, 0, 0, 0, 0]

]

Из заданной матрицы смежности написана программа для вывода списка ребер (Рисунок 2).

Листинг. Вывод списка ребер графа

def matrix_to_edge_list(peaks, matrix):

edge_list = []

for i in range(len(peaks)):

for j in range(len(peaks)):

if matrix[i][j] != 0:

edge_list.append((peaks[i], peaks[j], matrix[i][j]))

return edge_list

edge_list = matrix_to_edge_list(peaks, matrix)

for edge in edge_list:

print(edge[0], '->', edge[1], ':', edge[2])

Рисунок 2- Вывод списка ребер из заданной матрицы смежности

Написана программа для визуализации графа, результат программы показан на рисунке 3.

Листинг 3- Построение графа

G = nx.DiGraph()

G.add_nodes_from(peaks)

G.add_weighted_edges_from(edge_list)

plt.figure()

# Определение позиций узлов на графе с помощью алгоритма раскладки

pos = nx.circular_layout(G)

# Отрисовка графа с узлами размером 2000, с позициями pos и подписями для узлов

nx.draw(G, node_size=2000, pos=pos, with_labels=True)

# Получение атрибутов весов рёбер и отрисовка их меток

edge_labels = nx.get_edge_attributes(G, "weight")

nx.draw_networkx_edge_labels(G, pos, edge_labels)

plt.show()

Рисунок 3- Результат работы программы построения графа

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

Листинг 4- Представление графа в виде массива записей

def matrix_to_records(peaks, matrix):

records = []

num_vertices = len(peaks) # Количество вершин в графе

for i, label in enumerate(peaks):

# Находим детей текущей вершины (вершины, к которым есть ребра из текущей вершины)

children = [j for j in range(num_vertices) if matrix[i][j] != 0]

# Находим родителей текущей вершины (вершины, из которых есть ребра в текущую вершину)

parents = [j for j in range(num_vertices) if matrix[j][i] != 0]

# Находим всех соседей текущей вершины (вершины, с которыми текущая вершина связана)

neighbors = list(set(children + parents))

# Получаем веса исходящих рёбер из текущей вершины

outgoing_edge_weights = [matrix[i][j] for j in children]

# Получаем веса входящих рёбер в текущую вершину

incoming_edge_weights = [matrix[j][i] for j in parents]

# Веса инцидентных рёбер текущей вершины

incident_edge_weights = [matrix[i][j] for j in neighbors]

# Имена детей текущей вершины

children_names = [peaks[j] for j in children]

# Имена родителей текущей вершины

parents_names = [peaks[j] for j in parents]

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

record = {

'№ вершины': i, # Номер вершины

'Имя вершины': label, # Имя вершины

'Количество детей': len(children), # Количество детей текущей вершины

'Дети': children_names, # Имена детей текущей вершины

'Веса исходящих ребер': outgoing_edge_weights, # Веса исходящих рёбер из текущей вершины

'Количество родителей': len(parents), # Количество родителей текущей вершины

'Родители': parents_names, # Имена родителей текущей вершины

'Веса входящих ребер': incoming_edge_weights, # Веса входящих рёбер в текущую вершину

'Количество соседей': len(neighbors), # Количество соседей текущей вершины

'Соседи': [peaks[j] for j in neighbors], # Имена соседей текущей вершины

'Веса инцидентных ребер': incident_edge_weights # Веса инцидентных рёбер текущей вершины

}

records.append(record)

return pd.DataFrame(records)

df = matrix_to_records(peaks, matrix)

display(df)

Рисунок 4- Результат работы программы представления графа в виде массива записей

Для каждого представления написана подпрограмма для вывода всех соседей вершины графа (Рисунок 5-7)

Листинг 4- Поиск соседей вершины графа для матрицы смежности

def neighbors_matrix(vertex, matrix):

neighbors = []

num_vertices = len(matrix)

for i in range(num_vertices):

if matrix[vertex][i] != 0:

neighbors.append(i)

for i in range(num_vertices):

if matrix[i][vertex] != 0:

neighbors.append(i)

return neighbors

# vertex = int(input("Введите номер вершины для поиска соседей: "))

# result = neighbors_matrix(vertex, matrix)

# print(f"Соседи выбранной вершины: {result}")

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

Листинг 5- Поиск соседей вершины графа для списка ребер

def neighbors_edge_list(vertex, edge_list):

neighbors = []

for edge in edge_list:

if edge[0] == vertex:

neighbors.append(edge[1])

elif edge[1] == vertex:

neighbors.append(edge[0])

return neighbors

# vertex = input("Введите имя вершину для поиска соседей: ")

# result = neighbors_edge_list(vertex, edge_list)

# print(f"Соседи выбранной вершины: {result}")

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

Листинг 6- Поиск соседей вершины графа для массива записей

def neighbors_records(vertex, records):

for record in records:

if record['Имя вершины'] == vertex:

return record['Соседи']

# vertex = input("Введите имя вершины для поиска соседей: ")

# result = neighbors_records(vertex, df.to_dict('records'))

# print(f"Соседи выбранной вершины: {result}")

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

Также написаны подпрограммы для вывода ответа, образует ли заданная последовательность вершин цепь.

Листинг 7- Образует ли заданная последовательность вершин цепь для матрицы смежности

def is_chain_adjacency_matrix(vertices, matrix):

visited_edges = set() # множество для отслеживания посещенных рёбер

prev_vertex = None # переменная для хранения предыдущей вершины

for vertex in vertices:

prev_vertex = vertex

for i in range(len(vertices) - 1):

source = vertices[i]

target = vertices[i + 1]

if matrix[source][target] == 0 or (source, target) in visited_edges:

return False

visited_edges.add((source, target)) # Добавление ребра в множество посещенных рёбер

return True

chain = list(map(int, input("Введите последовательность вершин через пробел: ").split()))

print("Образует ли заданная последовательность вершин цепь:", is_chain_adjacency_matrix(chain, matrix))

Листинг 8- Образует ли заданная последовательность вершин цепь для списка ребер

def edgesList_is_chain(edges, vertices):

visited_edges = set() # Множество для отслеживания посещенных рёбер

for i in range(len(vertices) - 1):

edge = (vertices[i], vertices[i + 1])

if edge not in [(x[:2]) for x in edges]:

return False

if edge in visited_edges: # Проверка наличия повторяющегося ребра

return False

visited_edges.add(edge) # Добавление ребра в множество посещенных рёбер

return True

vertices= input("Введите последовательность имен вершин через пробел: ").split()

print("Образует ли заданная последовательность вершин цепь:", edgesList_is_chain(edge_list, vertices))

Листинг 9- Образует ли заданная последовательность вершин цепь для массива записей

def is_chain_records(vertices, records):

visited_edges = set() # множество для отслеживания посещенных рёбер

for i in range(len(vertices) - 1):

current_vertex = vertices[i]

next_vertex = vertices[i + 1]

found = False

for record in records:

if record['Имя вершины'] == current_vertex:

if next_vertex in record['Дети']:

edge = (current_vertex, next_vertex)

if edge in visited_edges: # Проверка

return False

else:

visited_edges.add(edge)

found = True

break

if not found:

return False

return True

# chain = input("Введите последовательность имен вершин через пробел: ").split()

# print("Образует ли заданная последовательность вершин цепь:", is_chain_records(chain, df.to_dict('records')))

Были написаны подпрограммы для вывода номеров вершин, сумма весов инцидентных ребер которых больше.

Листинг 10- Вывод номеров вершин, сумма весов инцидентных ребер которых больше для матрицы смежности

def matrix_nodes_with_large_incident_edges(matrix, threshold):

nodes = []

for i in range(len(matrix)):

incident_edges_sum = sum(matrix[i]) + sum(row[i] for row in matrix)

if incident_edges_sum > threshold:

nodes.append(i)

return nodes

# threshold = int(input("Введите пороговое значение суммы весов: "))

# print(matrix_nodes_with_large_incident_edges(matrix, threshold))

Листинг 11- Вывод номеров вершин, сумма весов инцидентных ребер которых больше для списка ребер

def nodes_with_large_incident_edges(edges, threshold):

incident_edges_sum = {}

for edge in edges:

source, target, weight = edge

incident_edges_sum[source] = incident_edges_sum.get(source, 0) + weight

incident_edges_sum[target] = incident_edges_sum.get(target, 0) + weight

nodes = [node for node, weight_sum in incident_edges_sum.items() if weight_sum > threshold]

return nodes

# threshold = int(input("Введите пороговое значение суммы весов: "))

# result = nodes_with_large_incident_edges(edge_list, threshold)

# print("Вершины с суммой весов инцидентных рёбер больше {}: {}".format(threshold, result))

Листинг 12- Вывод номеров вершин, сумма весов инцидентных ребер которых больше для массива записей

def df_nodes_with_large_incident_edges(df, threshold):

above_threshold = []

for index, row in df.iterrows():

weights_sum = sum(row['Веса инцидентных ребер'])

if weights_sum > threshold:

above_threshold.append(row['Имя вершины'])

return above_threshold

# threshold = int(input("Введите пороговое значение суммы весов: "))

# result = df_nodes_with_large_incident_edges(df, threshold)

# print("Вершины с суммой весов инцидентных рёбер больше {}: {}".format(threshold, result))

Написаны подпрограммы для вывода количества ребер в графе для всех представлений

Листинг 13- Вывод количества ребер в графе для матрицы смежности

def count_edges_adjacency_matrix(matrix):

count = 0

for row in matrix:

count += sum(1 for weight in row if weight != 0)

return count

# result = count_edges_adjacency_matrix(matrix)

# print("Количество ребер в графе:", result)

Листинг 14- Вывод количества ребер в графе для списка ребер

def count_edges_edge_list(edge_list):

return len(edge_list)

# count = count_edges_edge_list(edge_list)

# print("Количество рёбер в графе :", count)

Листинг 15- Вывод количества ребер в графе для массива записей

def df_count_edges(df):

return df['Количество детей'].sum()

# print(df_count_edges(df))

Для каждого представления посчитан размер содержащего их объекта в байтах и выведен в датафрейм (Рисунок 8).

Листинг 16- Подсчет размера содержащего объекта представлений в байтах.

import sys

import pandas as pd

size_of_matrix = sys.getsizeof(matrix)

size_of_edge_list = sys.getsizeof(edge_list)

size_of_records = sys.getsizeof(df)

sizes_df = pd.DataFrame({

'Представление': ['Матрица смежности', 'Список рёбер', 'Массив записей'],

'Размер в байтах': [size_of_matrix, size_of_edge_list, size_of_records]

})

print(sizes_df)

Рисунок 8- Датафрейм с размерами

Результат показывает, что Матрица смежности имеет наименьший размер, затем идет список ребер, а массив записей требует наибольшего объема памяти.

Далее подсчитано время выполнения каждой подпрограммы всех представлений и выведено на экран (Рисунок 9-11).

Рисунок 9- Результат работы программы для подсчета времени выполнения подпрограмм для матрицы смежности

Листинг 17- Подсчет времени выполнения подпрограмм для матрицы смежности

num_iterations = 10**6

vertex = 1

vertices = [0, 1, 5]

threshold = 5

print("Матрица смежности, время выполнения функций")

df_results = pd.DataFrame()

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

neighbors_matrix(vertex,matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "neighbors_matrix", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

is_chain_adjacency_matrix(vertices, matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': " is_chain_adjacency_matrix", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

matrix_nodes_with_large_incident_edges(matrix, threshold)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "matrix_nodes_with_large_incident_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

count_edges_adjacency_matrix(matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

#################################

pd.set_option('display.float_format', '{:.7f}'.format)

df_results = df_results.append({'Функция': "count_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

df_results

Рисунок 10 - Результат работы программы для подсчета времени выполнения подпрограмм для списка ребер

Листинг 18- Подсчет времени выполнения подпрограмм для списка ребер

num_iterations = 10**6

vertex = 1

vertices = [0, 1, 5]

threshold = 5

print("Матрица смежности, время выполнения функций")

df_results = pd.DataFrame()

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

neighbors_matrix(vertex,matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "neighbors_matrix", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

is_chain_adjacency_matrix(vertices, matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': " is_chain_adjacency_matrix", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

matrix_nodes_with_large_incident_edges(matrix, threshold)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "matrix_nodes_with_large_incident_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

count_edges_adjacency_matrix(matrix)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

#################################

pd.set_option('display.float_format', '{:.7f}'.format)

df_results = df_results.append({'Функция': "count_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

df_results

Рисунок 11 - Результат работы программы для подсчета времени выполнения подпрограмм для массива записей

Листинг 19- Подсчет времени выполнения подпрограмм для списка ребер

num_iterations = 10**6

total_time = 0

vertex= 'A'

vertices = ['A', 'B', 'C']

threshold = 30

print("Датафрейм, скорость выполнения функций")

df_results = pd.DataFrame()

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

neighbors_records(vertex, df.to_dict('records'))

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "neighbors_records", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

is_chain_records(vertices, df.to_dict('records'))

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': " is_chain_records", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

df_nodes_with_large_incident_edges(df, threshold)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

df_results = df_results.append({'Функция': "df_nodes_with_large_incident_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

#################################

total_time = 0

for i in range(num_iterations):

start_time = time.time()

df_count_edges(df)

end_time = time.time()

total_time += end_time - start_time

average_time = total_time / num_iterations

#################################

df_results = df_results.append({'Функция': "df_count_edges", 'Время выполнения, сек': average_time}, ignore_index=True)

df_results

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

Вывод:

В процессе выполнения лабораторной работы получены следующие выводы о целесообразности хранения структуры графа в рассмотренных представлениях:

Матрица смежности:

Занимает минимальный объем памяти при хранении.

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

Список ребер:

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

Массив записей:

Обеспечивает более удобный доступ к дополнительным атрибутам вершин или ребер, таким как веса.

Требует больше памяти, особенно для больших графов.

Соседние файлы в предмете Построение и анализ графовых моделей