This commit is contained in:
xalva 2026-05-20 15:19:09 +03:00
parent 56509603c7
commit f4e8b9732e
16 changed files with 1471 additions and 231 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

View File

@ -1,91 +0,0 @@
Структура,Режим,Операция,Время (сек)
LinkedList,shuffled,insert,0.154480
LinkedList,shuffled,search,0.001006
LinkedList,shuffled,delete,0.000890
LinkedList,shuffled,insert,1.084111
LinkedList,shuffled,search,0.000904
LinkedList,shuffled,delete,0.000629
LinkedList,shuffled,insert,0.131441
LinkedList,shuffled,search,0.001123
LinkedList,shuffled,delete,0.000622
LinkedList,shuffled,insert,0.163422
LinkedList,shuffled,search,0.000789
LinkedList,shuffled,delete,0.000530
LinkedList,shuffled,insert,0.145036
LinkedList,shuffled,search,0.000570
LinkedList,shuffled,delete,0.000318
LinkedList,sorted,insert,24.938719
LinkedList,sorted,search,0.106848
LinkedList,sorted,delete,0.096196
LinkedList,sorted,insert,24.883229
LinkedList,sorted,search,0.106409
LinkedList,sorted,delete,0.094658
LinkedList,sorted,insert,24.408379
LinkedList,sorted,search,0.115546
LinkedList,sorted,delete,0.099195
LinkedList,sorted,insert,24.421941
LinkedList,sorted,search,0.102282
LinkedList,sorted,delete,0.092586
LinkedList,sorted,insert,24.125530
LinkedList,sorted,search,0.106052
LinkedList,sorted,delete,0.093177
HashTable,shuffled,insert,0.024262
HashTable,shuffled,search,0.000651
HashTable,shuffled,delete,0.000211
HashTable,shuffled,insert,0.022815
HashTable,shuffled,search,0.000259
HashTable,shuffled,delete,0.000115
HashTable,shuffled,insert,0.026916
HashTable,shuffled,search,0.000264
HashTable,shuffled,delete,0.000115
HashTable,shuffled,insert,0.022850
HashTable,shuffled,search,0.000251
HashTable,shuffled,delete,0.000115
HashTable,shuffled,insert,0.023054
HashTable,shuffled,search,0.000261
HashTable,shuffled,delete,0.000114
HashTable,sorted,insert,0.021750
HashTable,sorted,search,0.000246
HashTable,sorted,delete,0.000110
HashTable,sorted,insert,0.022438
HashTable,sorted,search,0.000248
HashTable,sorted,delete,0.000111
HashTable,sorted,insert,0.021394
HashTable,sorted,search,0.000230
HashTable,sorted,delete,0.000106
HashTable,sorted,insert,0.022591
HashTable,sorted,search,0.000285
HashTable,sorted,delete,0.000125
HashTable,sorted,insert,0.021119
HashTable,sorted,search,0.000272
HashTable,sorted,delete,0.000122
BST,shuffled,insert,0.054849
BST,shuffled,search,0.000554
BST,shuffled,delete,0.000293
BST,shuffled,insert,0.053888
BST,shuffled,search,0.000415
BST,shuffled,delete,0.000260
BST,shuffled,insert,0.053399
BST,shuffled,search,0.000407
BST,shuffled,delete,0.000256
BST,shuffled,insert,0.056071
BST,shuffled,search,0.000412
BST,shuffled,delete,0.000261
BST,shuffled,insert,0.053024
BST,shuffled,search,0.000409
BST,shuffled,delete,0.000285
BST,sorted,insert,24.942325
BST,sorted,search,0.108153
BST,sorted,delete,0.094860
BST,sorted,insert,25.196583
BST,sorted,search,0.109160
BST,sorted,delete,0.096340
BST,sorted,insert,24.691507
BST,sorted,search,0.115560
BST,sorted,delete,0.094962
BST,sorted,insert,24.461825
BST,sorted,search,0.103381
BST,sorted,delete,0.095198
BST,sorted,insert,24.798636
BST,sorted,search,0.101888
BST,sorted,delete,0.093775
1 Структура Режим Операция Время (сек)
2 LinkedList shuffled insert 0.154480
3 LinkedList shuffled search 0.001006
4 LinkedList shuffled delete 0.000890
5 LinkedList shuffled insert 1.084111
6 LinkedList shuffled search 0.000904
7 LinkedList shuffled delete 0.000629
8 LinkedList shuffled insert 0.131441
9 LinkedList shuffled search 0.001123
10 LinkedList shuffled delete 0.000622
11 LinkedList shuffled insert 0.163422
12 LinkedList shuffled search 0.000789
13 LinkedList shuffled delete 0.000530
14 LinkedList shuffled insert 0.145036
15 LinkedList shuffled search 0.000570
16 LinkedList shuffled delete 0.000318
17 LinkedList sorted insert 24.938719
18 LinkedList sorted search 0.106848
19 LinkedList sorted delete 0.096196
20 LinkedList sorted insert 24.883229
21 LinkedList sorted search 0.106409
22 LinkedList sorted delete 0.094658
23 LinkedList sorted insert 24.408379
24 LinkedList sorted search 0.115546
25 LinkedList sorted delete 0.099195
26 LinkedList sorted insert 24.421941
27 LinkedList sorted search 0.102282
28 LinkedList sorted delete 0.092586
29 LinkedList sorted insert 24.125530
30 LinkedList sorted search 0.106052
31 LinkedList sorted delete 0.093177
32 HashTable shuffled insert 0.024262
33 HashTable shuffled search 0.000651
34 HashTable shuffled delete 0.000211
35 HashTable shuffled insert 0.022815
36 HashTable shuffled search 0.000259
37 HashTable shuffled delete 0.000115
38 HashTable shuffled insert 0.026916
39 HashTable shuffled search 0.000264
40 HashTable shuffled delete 0.000115
41 HashTable shuffled insert 0.022850
42 HashTable shuffled search 0.000251
43 HashTable shuffled delete 0.000115
44 HashTable shuffled insert 0.023054
45 HashTable shuffled search 0.000261
46 HashTable shuffled delete 0.000114
47 HashTable sorted insert 0.021750
48 HashTable sorted search 0.000246
49 HashTable sorted delete 0.000110
50 HashTable sorted insert 0.022438
51 HashTable sorted search 0.000248
52 HashTable sorted delete 0.000111
53 HashTable sorted insert 0.021394
54 HashTable sorted search 0.000230
55 HashTable sorted delete 0.000106
56 HashTable sorted insert 0.022591
57 HashTable sorted search 0.000285
58 HashTable sorted delete 0.000125
59 HashTable sorted insert 0.021119
60 HashTable sorted search 0.000272
61 HashTable sorted delete 0.000122
62 BST shuffled insert 0.054849
63 BST shuffled search 0.000554
64 BST shuffled delete 0.000293
65 BST shuffled insert 0.053888
66 BST shuffled search 0.000415
67 BST shuffled delete 0.000260
68 BST shuffled insert 0.053399
69 BST shuffled search 0.000407
70 BST shuffled delete 0.000256
71 BST shuffled insert 0.056071
72 BST shuffled search 0.000412
73 BST shuffled delete 0.000261
74 BST shuffled insert 0.053024
75 BST shuffled search 0.000409
76 BST shuffled delete 0.000285
77 BST sorted insert 24.942325
78 BST sorted search 0.108153
79 BST sorted delete 0.094860
80 BST sorted insert 25.196583
81 BST sorted search 0.109160
82 BST sorted delete 0.096340
83 BST sorted insert 24.691507
84 BST sorted search 0.115560
85 BST sorted delete 0.094962
86 BST sorted insert 24.461825
87 BST sorted search 0.103381
88 BST sorted delete 0.095198
89 BST sorted insert 24.798636
90 BST sorted search 0.101888
91 BST sorted delete 0.093775

View File

@ -1,44 +0,0 @@
Анализ по пунктам задания
Влияние порядка входных данных на вставку в BST
На отсортированных данных BST превращается в связный список
(все узлы добавляются только в правое поддерево),
поэтому каждая операция вставки требует прохода по всем ранее вставленным
элементам. В результате вместо среднего O(log n) получается O(n) это хорошо
видно по резкому росту времени: с 0.02 с до ~2 с. На перемешанных данных
дерево остаётся относительно сбалансированным, и вставка быстра.
Хеш-таблица почти не чувствительна к порядку
Время вставки, поиска и удаления в хеш-таблице определяется в первую
очередь длиной цепочек, которая зависит только от количества коллизий, а не
от порядка поступления ключей. Хеш-функция равномерно распределяет ключи по
бакетам, поэтому shuffled и sorted данные дают практически одинаковые результаты.
Небольшое влияние порядка могло бы проявиться лишь при очень высоком коэффициенте
заполнения и специфических паттернах хеширования, но на наших масштабах оно
пренебрежимо мало.
Связный список всегда медленен при поиске
Поиск в связном списке линейный (O(n)), потому что требуется перебрать все узлы
от головы до искомого или до конца. В нашем эксперименте поиск 110 имён занимал
в среднем 0.03 с, что на два порядка медленнее хеш-таблицы и BST в нормальном
режиме. Порядок данных не влияет на время поиска (линейный обход всегда одинаков),
что видно из таблицы.
Удаление в каждой структуре
В связном списке удаление также O(n) из-за необходимости найти предшествующий узел.
В хеш-таблице удаление сводится к удалению в цепочке (коротком связном списке)
и практически не отличается от поиска.
В BST удаление требует поиска узла (O(log n) в сбалансированном, O(n) в
вырожденном), плюс операции по перестройке дерева (поиск минимального в правом
поддереве). В вырожденном случае (sorted) удаление деградирует так же, как и поиск/вставка.
Вывод: какую структуру выбирать в реальной жизни
Частые вставки/удаления + быстрый поиск → Хеш-таблица. Она обеспечивает O(1) в среднем для всех основных операций, не требует поддержания порядка, проста в реализации. Идеально для словарей, кэшей, индексов баз данных.
Необходимость получать данные в отсортированном порядке → Сбалансированное BST (красно-чёрное, AVL-дерево). Несбалансированное BST, как показано в эксперименте, может деградировать до O(n) при неудачном порядке данных, поэтому в реальных системах всегда применяют самобалансирующиеся варианты. Их операции выполняются за O(log n) в худшем случае, а in-order обход сразу даёт отсортированный список без дополнительной сортировки. Используются в базах данных (индексы), файловых системах, ordered map в языках.
Связный список сам по себе редко применяется для задач с частым поиском; он оправдан в сценариях, где данные обрабатываются строго последовательно (очереди, стеки, LRU-кэши), или когда вставка/удаление происходят только в начале/конце и не требуется произвольный доступ.
Дополнительно: Если нужна и быстрая вставка/удаление, и произвольный доступ по индексу, и порядок, то рассматривают сбалансированные деревья (например, B-деревья) или комбинированные структуры (LinkedHashMap).
Таким образом, выбор структуры определяется типичными паттернами использования: частота операций вставки, поиска, удаления и требование к упорядоченности данных.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,55 @@
Структура,Режим,Повтор,Операция,Время (сек)
LinkedList,shuffled,1,insert,0.5023735999711789
LinkedList,shuffled,1,search,0.022223800013307482
LinkedList,shuffled,1,delete,0.010106799949426204
LinkedList,shuffled,2,insert,0.5151404999778606
LinkedList,shuffled,2,search,0.023844500014092773
LinkedList,shuffled,2,delete,0.010028599994257092
LinkedList,shuffled,3,insert,0.5328615000471473
LinkedList,shuffled,3,search,0.020557800016831607
LinkedList,shuffled,3,delete,0.012162799946963787
LinkedList,sorted,1,insert,0.4577932999818586
LinkedList,sorted,1,search,0.017212599981576204
LinkedList,sorted,1,delete,0.012185800005681813
LinkedList,sorted,2,insert,0.43183969997335225
LinkedList,sorted,2,search,0.01829650002764538
LinkedList,sorted,2,delete,0.012130599992815405
LinkedList,sorted,3,insert,0.436789300001692
LinkedList,sorted,3,search,0.017460400005802512
LinkedList,sorted,3,delete,0.012465099978726357
HashTable,shuffled,1,insert,0.0032562999986112118
HashTable,shuffled,1,search,9.469996439293027e-05
HashTable,shuffled,1,delete,5.15999854542315e-05
HashTable,shuffled,2,insert,0.0031429000082425773
HashTable,shuffled,2,search,9.000004502013326e-05
HashTable,shuffled,2,delete,4.360004095360637e-05
HashTable,shuffled,3,insert,0.003212600015103817
HashTable,shuffled,3,search,0.00010830000974237919
HashTable,shuffled,3,delete,4.650000482797623e-05
HashTable,sorted,1,insert,0.0030796999926678836
HashTable,sorted,1,search,8.420000085607171e-05
HashTable,sorted,1,delete,4.730001091957092e-05
HashTable,sorted,2,insert,0.0030180999892763793
HashTable,sorted,2,search,9.079999290406704e-05
HashTable,sorted,2,delete,5.299999611452222e-05
HashTable,sorted,3,insert,0.0029779999749734998
HashTable,sorted,3,search,8.510000770911574e-05
HashTable,sorted,3,delete,6.589997792616487e-05
BST,shuffled,1,insert,0.011618499993346632
BST,shuffled,1,search,0.00031289999606087804
BST,shuffled,1,delete,0.0002456999500282109
BST,shuffled,2,insert,0.021565500006545335
BST,shuffled,2,search,0.00032350001856684685
BST,shuffled,2,delete,0.0002101999707520008
BST,shuffled,3,insert,0.011865400010719895
BST,shuffled,3,search,0.0003497999859973788
BST,shuffled,3,delete,0.0002114999806508422
BST,sorted,1,insert,1.961912199971266
BST,sorted,1,search,0.025325599999632686
BST,sorted,1,delete,0.03309909999370575
BST,sorted,2,insert,1.8450072000268847
BST,sorted,2,search,0.025074300006963313
BST,sorted,2,delete,0.03284020000137389
BST,sorted,3,insert,1.8502263000118546
BST,sorted,3,search,0.028948499995749444
BST,sorted,3,delete,0.040639499959070235
1 Структура Режим Повтор Операция Время (сек)
2 LinkedList shuffled 1 insert 0.5023735999711789
3 LinkedList shuffled 1 search 0.022223800013307482
4 LinkedList shuffled 1 delete 0.010106799949426204
5 LinkedList shuffled 2 insert 0.5151404999778606
6 LinkedList shuffled 2 search 0.023844500014092773
7 LinkedList shuffled 2 delete 0.010028599994257092
8 LinkedList shuffled 3 insert 0.5328615000471473
9 LinkedList shuffled 3 search 0.020557800016831607
10 LinkedList shuffled 3 delete 0.012162799946963787
11 LinkedList sorted 1 insert 0.4577932999818586
12 LinkedList sorted 1 search 0.017212599981576204
13 LinkedList sorted 1 delete 0.012185800005681813
14 LinkedList sorted 2 insert 0.43183969997335225
15 LinkedList sorted 2 search 0.01829650002764538
16 LinkedList sorted 2 delete 0.012130599992815405
17 LinkedList sorted 3 insert 0.436789300001692
18 LinkedList sorted 3 search 0.017460400005802512
19 LinkedList sorted 3 delete 0.012465099978726357
20 HashTable shuffled 1 insert 0.0032562999986112118
21 HashTable shuffled 1 search 9.469996439293027e-05
22 HashTable shuffled 1 delete 5.15999854542315e-05
23 HashTable shuffled 2 insert 0.0031429000082425773
24 HashTable shuffled 2 search 9.000004502013326e-05
25 HashTable shuffled 2 delete 4.360004095360637e-05
26 HashTable shuffled 3 insert 0.003212600015103817
27 HashTable shuffled 3 search 0.00010830000974237919
28 HashTable shuffled 3 delete 4.650000482797623e-05
29 HashTable sorted 1 insert 0.0030796999926678836
30 HashTable sorted 1 search 8.420000085607171e-05
31 HashTable sorted 1 delete 4.730001091957092e-05
32 HashTable sorted 2 insert 0.0030180999892763793
33 HashTable sorted 2 search 9.079999290406704e-05
34 HashTable sorted 2 delete 5.299999611452222e-05
35 HashTable sorted 3 insert 0.0029779999749734998
36 HashTable sorted 3 search 8.510000770911574e-05
37 HashTable sorted 3 delete 6.589997792616487e-05
38 BST shuffled 1 insert 0.011618499993346632
39 BST shuffled 1 search 0.00031289999606087804
40 BST shuffled 1 delete 0.0002456999500282109
41 BST shuffled 2 insert 0.021565500006545335
42 BST shuffled 2 search 0.00032350001856684685
43 BST shuffled 2 delete 0.0002101999707520008
44 BST shuffled 3 insert 0.011865400010719895
45 BST shuffled 3 search 0.0003497999859973788
46 BST shuffled 3 delete 0.0002114999806508422
47 BST sorted 1 insert 1.961912199971266
48 BST sorted 1 search 0.025325599999632686
49 BST sorted 1 delete 0.03309909999370575
50 BST sorted 2 insert 1.8450072000268847
51 BST sorted 2 search 0.025074300006963313
52 BST sorted 2 delete 0.03284020000137389
53 BST sorted 3 insert 1.8502263000118546
54 BST sorted 3 search 0.028948499995749444
55 BST sorted 3 delete 0.040639499959070235

View File

@ -0,0 +1,15 @@
В ходе экспериментов было показано, что производительность структуры данных сильно зависит
от её внутреннего устройства и характера входных данных.
BST работает быстро на случайных данных, но при отсортированном порядке деградирует почти до
связного списка, из-за чего время вставки и удаления резко увеличивается. Хеш-таблица
практически не зависит от порядка входных данных, так как доступ к элементам происходит через
хеш-функцию, поэтому она показала лучшие результаты при поиске и вставке. Связный список
оказался самым медленным при поиске, так как требует последовательного обхода элементов.
Удаление также работает по-разному: в связном списке и BST сначала требуется поиск элемента,
а в хеш-таблице удаление обычно выполняется быстрее за счёт обращения к нужному бакету.
На практике хеш-таблицы лучше подходят для частого поиска и вставки данных, BST — когда
важно хранить элементы в отсортированном виде, а связные списки полезны в более простых
задачах, где структура данных часто изменяется и не требуется быстрый поиск.

View File

@ -1,7 +1,7 @@
import sys
sys.setrecursionlimit(20000)
sys.setrecursionlimit(30000)
csv_path = 'C:/Users/xalva/2026-rff_mp/stepinim/docs/data/results.csv'
csv_path = '/stepinim/docs/data/lab1_results.csv'
#Связный список
def ll_insert(head, name, phone):
@ -131,125 +131,303 @@ def bst_list_all(root):
inorder(root)
return result
#ТЕСТ
# ============================================================
# TECT
# ============================================================
import os
import random
random.seed(42)
N = 10000
base_records = [(f"User_{i:05d}", f"123-{i:05d}") for i in range(N)]
records_shuffled = base_records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(base_records, key=lambda x: x[0])
# 100 случайных существующих имён из всего набора
random_sample = random.sample(base_records, 100)
search_existing = [name for name, _ in random_sample]
# 10 несуществующих
search_nonexist = [f"None_{i}" for i in range(10)]
# 50 случайных для удаления
delete_sample = random.sample(base_records, 50)
delete_names = [name for name, _ in delete_sample]
import time
import csv
import statistics
import pandas as pd
import matplotlib.pyplot as plt
# ============================================================
# ПОДГОТОВКА ПАПОК
# ============================================================
DATA_DIR = os.path.join("docs", "data")
os.makedirs(DATA_DIR, exist_ok=True)
csv_path = os.path.join(DATA_DIR, "lab1_results.csv")
graph_path = os.path.join(DATA_DIR, "lab1_graph.png")
# ============================================================
# ТЕСТОВЫЕ ДАННЫЕ
# ============================================================
random.seed(42)
N = 3000
base_records = [
(f"User_{i:05d}", f"123-{i:05d}")
for i in range(N)
]
records_shuffled = base_records.copy()
random.shuffle(records_shuffled)
records_sorted = sorted(base_records, key=lambda x: x[0])
# Поиск
search_existing = [
name for name, _ in random.sample(base_records, 100)
]
search_nonexist = [
f"None_{i}"
for i in range(10)
]
# Удаление
delete_names = [
name for name, _ in random.sample(base_records, 50)
]
# ============================================================
# СОЗДАНИЕ СТРУКТУР
# ============================================================
def build_structure(records, struct_type):
if struct_type == "ll":
structure = None
for name, phone in records:
structure = ll_insert(structure, name, phone)
return structure
elif struct_type == "ht":
structure = [None] * HASH_SIZE
for name, phone in records:
ht_insert(structure, name, phone)
return structure
elif struct_type == "bst":
structure = None
for name, phone in records:
structure = bst_insert(structure, name, phone)
return structure
def measure_operations(records, struct_type):
results = []
for rep in range(5):
if struct_type == 'll':
head = None
elif struct_type == 'ht':
head = [None] * HASH_SIZE
else:
root = None
# ============================================================
# INSERT
# ============================================================
start = time.perf_counter()
if struct_type == 'll':
for name, phone in records:
head = ll_insert(head, name, phone)
elif struct_type == 'ht':
for name, phone in records:
ht_insert(head, name, phone)
else:
for name, phone in records:
root = bst_insert(root, name, phone)
insert_time = time.perf_counter() - start
results.append((rep + 1, 'insert', insert_time))
def measure_insert(records, struct_type):
start = time.perf_counter()
if struct_type == 'll':
for name in search_existing + search_nonexist:
ll_find(head, name)
elif struct_type == 'ht':
for name in search_existing + search_nonexist:
ht_find(head, name)
else:
for name in search_existing + search_nonexist:
bst_find(root, name)
search_time = time.perf_counter() - start
results.append((rep + 1, 'search', search_time))
start = time.perf_counter()
start = time.perf_counter()
if struct_type == 'll':
for name in delete_names:
head = ll_delete(head, name)
elif struct_type == 'ht':
for name in delete_names:
ht_delete(head, name)
else:
for name in delete_names:
root = bst_delete(root, name)
delete_time = time.perf_counter() - start
results.append((rep + 1, 'delete', delete_time))
return results
build_structure(records, struct_type)
end = time.perf_counter()
return end - start
# ============================================================
# SEARCH
# ============================================================
def measure_search(records, struct_type):
structure = build_structure(records, struct_type)
start = time.perf_counter()
if struct_type == "ll":
for name in search_existing + search_nonexist:
ll_find(structure, name)
elif struct_type == "ht":
for name in search_existing + search_nonexist:
ht_find(structure, name)
elif struct_type == "bst":
for name in search_existing + search_nonexist:
bst_find(structure, name)
end = time.perf_counter()
return end - start
# ============================================================
# DELETE
# ============================================================
def measure_delete(records, struct_type):
structure = build_structure(records, struct_type)
start = time.perf_counter()
if struct_type == "ll":
for name in delete_names:
structure = ll_delete(structure, name)
elif struct_type == "ht":
for name in delete_names:
ht_delete(structure, name)
elif struct_type == "bst":
for name in delete_names:
structure = bst_delete(structure, name)
end = time.perf_counter()
return end - start
# ============================================================
# ЗАМЕРЫ
# ============================================================
all_data = []
for struct_name, mode_label, records in [
("LinkedList", "shuffled", records_shuffled),
("LinkedList", "sorted", records_sorted),
("HashTable", "shuffled", records_shuffled),
("HashTable", "sorted", records_sorted),
("BST", "shuffled", records_shuffled),
("BST", "sorted", records_sorted),
]:
for rep, op, t in measure_operations(records, struct_name.split('d')[0].lower()[:2] if 'Linked' in struct_name else ('ht' if 'Hash' in struct_name else 'bst')):
all_data.append([struct_name, mode_label, op, f"{t:.6f}"])
experiments = [
("LinkedList", "ll"),
("HashTable", "ht"),
("BST", "bst")
]
modes = [
("shuffled", records_shuffled),
("sorted", records_sorted)
]
for struct_name, struct_type in experiments:
for mode_name, records in modes:
for rep in range(1, 4):
insert_time = measure_insert(records, struct_type)
search_time = measure_search(records, struct_type)
delete_time = measure_delete(records, struct_type)
all_data.append([
struct_name,
mode_name,
rep,
"insert",
insert_time
])
all_data.append([
struct_name,
mode_name,
rep,
"search",
search_time
])
all_data.append([
struct_name,
mode_name,
rep,
"delete",
delete_time
])
# ============================================================
# CSV
# ============================================================
with open(csv_path, "w", newline="", encoding="utf-8") as f:
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(["Структура", "Режим", "Операция", "Время (сек)"])
writer.writerow([
"Структура",
"Режим",
"Повтор",
"Операция",
"Время (сек)"
])
writer.writerows(all_data)
print("CSV сохранён в docs/data/results.csv")
print(f"CSV сохранён: {csv_path}")
import matplotlib.pyplot as plt
import pandas as pd
# ============================================================
# ГРАФИК
# ============================================================
df = pd.read_csv(csv_path)
df_avg = df.groupby(['Структура', 'Режим', 'Операция'])['Время (сек)'].mean().reset_index()
df_avg = (
df.groupby(
["Структура", "Режим", "Операция"]
)["Время (сек)"]
.mean()
.reset_index()
)
fig, ax = plt.subplots(figsize=(12, 6))
ops = ["insert", "search", "delete"]
fig, ax = plt.subplots(figsize=(10,6))
ops = ['insert', 'search', 'delete']
x = range(len(ops))
width = 0.12
for i, (struct, mode) in enumerate([('LinkedList','shuffled'),('LinkedList','sorted'),
('HashTable','shuffled'),('HashTable','sorted'),
('BST','shuffled'),('BST','sorted')]):
subset = df_avg[(df_avg['Структура']==struct) & (df_avg['Режим']==mode)]
times = [subset[subset['Операция']==op]['Время (сек)'].values[0] for op in ops]
ax.bar([p + i*width for p in x], times, width, label=f"{struct} ({mode})")
configs = [
("LinkedList", "shuffled"),
("LinkedList", "sorted"),
("HashTable", "shuffled"),
("HashTable", "sorted"),
("BST", "shuffled"),
("BST", "sorted")
]
for i, (struct, mode) in enumerate(configs):
subset = df_avg[
(df_avg["Структура"] == struct)
&
(df_avg["Режим"] == mode)
]
times = [
subset[
subset["Операция"] == op
]["Время (сек)"].values[0]
for op in ops
]
ax.bar(
[p + i * width for p in x],
times,
width,
label=f"{struct} ({mode})"
)
ax.set_xticks([p + 2.5 * width for p in x])
ax.set_xticks([p + 2.5*width for p in x])
ax.set_xticklabels(ops)
ax.set_ylabel('Среднее время (сек)')
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.set_ylabel("Среднее время (сек)")
ax.set_title("Сравнение структур данных")
ax.legend(
bbox_to_anchor=(1.05, 1),
loc="upper left"
)
plt.tight_layout()
plt.savefig('C:/Users/xalva/2026-rff_mp/stepinim/docs/data/grafik.png')
plt.savefig(graph_path)
print(f"График сохранён: {graph_path}")
plt.show()

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,20 @@
S
E

View File

@ -0,0 +1,100 @@
####################################################################################################
#S # # ### ## ## # # ### # # ## # # ### ## #### # ## ## # #####
# # # # ## ## # # # # # # # # # ##### # ## # # ## # ## # #
## # ## ### ## #### ## # # # ## #### # ## #### ## ### # # # ## # # ## #
### # ## ## ### # # #### # # # # ## ## ###### ## ## # # ### # ###
## # # # ### # # ## ### ### # # ## # # # ## # # ####### # #
###### ### ## # # # # # # ### ### ## ### ### ##### # # ## # # ## ## ####
#### # ## # # ## # ## ## ## # ## # # ## ## # # # # ## ###
# # # # # ###### # # ## #### # # # ## # # ### ##### ## # #
# ### # # # # ## ### # # ## #### # # # ###### # # ###### # ## #
## ## ## ### # # ### # # ## # ## ### ### ## ## # ### # # #
## # ## # # ## # ### # ## # ### ## # ### ### ### # ## ## # ### # # #
# # # # # ### # ## # # # ## # # # # # # # # # # ### ## # # ## # # #
## # # # #### ## # # # # # # ### # # ##### # # # # # # # # #
# ## ## # ## # # #### # # ## #### # ## # # # # # # # # #
# # ### # # # ## # # # # ## ## # # # # ## # # # # ## ## #### # ## # # #
# # ### # # ## # ## #### #### ## # #### # ### ## # # # # ## ## #
##### # # # # # ## # ## # ### # # # # # # # # # # ### ## # ## ## #### # # #
# # # #### ## ## ## # # # # # # # # # ## # # ## # ### # # ## # # # ##
### # ### ####### ### # # # # # ## ## # ## # ## # ## ###
# # ### ######## ### ## # # # # # # ## ## # ## ### # # # ### ### ###
# # ## ## # # ### ### ## # # ## # # # # # ## ## ### # # #### #### ###
# ### ## ### #### # ## # # ## # ## # ## ### # # # # ### # ## ###
#### ## # ### # # ## ### ## # # # # # # # ## # ## ## # # # # ## ## #
### ## # # #### ## ## ##### ## # # # ## # ## # # # # # # # # ## # ## # #
# # # # # # # ## ## # ### # ## ## # ## # # # ## # # ## ## # # # # ## #
# # ###### # # # # # # # # ## ## # # # ## # # # # # # ## # # ## # # ##
# # # # # ## # # # # # # # # ## # ## # #### # ### #### ## # # ## ##
## # # ## # ### # #### ## # ### ## # # # # ### # ## # # # # #
# # ### # ### # ## # ## ## # # # ### ##### ## ### # ## ### # # # ## ###### #
## # # # ## # # #### # # ## # ## # # # # # # # # # # # # #
## # # # ## #### # ### ### ## # ## ##### # # # # # # # # # # ##### # # ##
# ## ## # # # # ### ## # # ### ### ## # # # # ## # # ## ### # # # ###
# ## ## ## ### # # #### # ## ## ## ### ### # # ## ##
## # # ##### #### # # ## ## ### # # # # # # # # # # ### # #### #
# # # # # # #### # # ## # # # ## # # # # # # # ### # # ##
## ### ## ### # ## ## ### # ## # # # # # # # # # # #### ## ##
## # ### ## ## # # ### # # #### # # # ### # # # # # # #### # #
# # # ## ### # # # # # # ## ## # # ## ## # ## # # # # ### ## # #
# ### # ### ## # # # # # # ## # ###### # ## ## ## # ## ## ## # ## # #
# # # # # ## # # # # # # ## # # # ###### # # ### # #
# # ## # # ## # # ## ### # ## ## ## # # # # # # # # # ## # # #
## #### ## ### #### # # ### # # # # # ## # ## # # ##### ## ## # # ## #
## # # # ## # ## # # ## # #### # # ### # #### # ## # ##### # # # #
# # # # # ## #### # # ## # # # # ## # ## ## # # # ## # # ## ## ### # #
# # # ## # ## # # ## ## # ## # # ## # ## # # # ##### #
## #### ## ##### # ### # ### # # ## # ## # # # # # # # ## # ### # ## #
# # # # # ## # ## # ## ## # # ### # ####### # # ## # ### ## ## ## ## ## #
### ## # ## ## # ## # ### ### # ### # # # ###### ### ### # # # ## ### # #
## # ## ## # ## ### # # # # ## # ### # ### ## # ## # # # # #
# # ## ## ## # # # # ## # # ## # ## # # # ## # # # # # # # # ## # # #
# # # ## # ## ## ## #### ## ## ## ### ##### ###### ## ### # # #
# # ## # # # # ## # # # # # ## # #### ## # ## # ### # ## #
# # ## # # # ### # # # # ## ### ## # ## # # # # # ## # # # ### ## # #
# ## # # # ## # # # # # ## # # # # ## # # # # ###### ## ### # #
# ## ## # ### # # ## ####### ## # ####### # # # # ## # ### # ## # # #
## ### ## ### # ## # # ## ### # ### ### # # # ## # # ## # ### # #
# # # ### ## # # ## # ## ## # ### # # # ### ###### # # # #
## # ### ## # ### ## ## # ## # ## ## ### ## ### ### ## ## # ## #
# # #### ## # # ## ## # #### # ### # ## ## # # # # # ## # #
# # # # #### # # # ### ## # ## ## ## # # ## # ##### # # # # # ## # #### # # #
# # ### # # ## #### # # # # ## ## # ## ## ### # ## ## ## # ## #
## # ## # # # # # ## ## # ## ## ## # ## # #### ## # ## #
# # # ## # ## # # # # ### # # ## ## # # # # # ### # ### ## # # ## #
# # # ### # # ## ## # # ## # ### # # ### # ## # ### # ## ## ## # #
### # ## # # #### ### ## # ## # ## # ## #### # # ###### # # #### # ## #
# # # # # # ### # # # # ## #### ### # # # # # ## # # # #### # #### # #
## ## # ## # # # # # ## # ### #### # ## ### # ##### ### ## ### # # #
# # # ####### ## ### # ####### # # # ####### # # # ### # # ## # # # #
## # # ## ## ## # ### ## ## ### ## ## # # ## # ## ## # # # # #
### # # #### ### # # ## # ## # # # #### # # # # # # # # ## # ## #
# ##### # ## ### ## #### # # # ## #### # # # # ## # # ## # #
# # # # # ### # ## #### # # # ### # # # # ## # # # # # # #
# # ## ## #### # # ### ## ## ### ### ## ## # # # # # ## # ## ### #
## # # ## # # ## # ## # ### # ### # # # ## # ### #### # ## #
# ## # # # # ## ## # # # # ## # ### ## ###### ### # ## ### # # # # # #
### # # # # #### # # # # ### ##### # ## # ## # # ## # # # ## #
# # # # ##### # # #### # # # ## # ## # # ## ### # # ## # #### #
### ## # # # ## # # ## # # ## # # # ### ## ## # ### # # ## # # # # #
#### # # # # ## # # # ## ### # # # ### # ## # # ## # # # ## # #
# # # ## # ## # # ## ### # ## ## # ## # ## ## # # # ## # # ### ## ## ## #
# # # ## # # # ## # ## ## # ## # ## ## #### #### # ## # # #
## # # #### ### ## # #### ### ### # ## # # ## ## # ## # ### #
# ## ##### # # ## ### ### # # # # ### #### ## ## ## ## ### ## # #
# # ## # ## ## # # ### # # # # ## # ### ## ## ## ## ## # # # #
# #### # # #### # ## ### ###### # # # ## # # # # # # # # # #### ## # # # #
# #### # ### ##### # ## ## # # ## ## # ## ## # ## ### # # # # # # #
# # ## # # # ## # # ## ## # # # # ## # # # ## ### # ## # #
### # ## # # # ## # # # #### ##### # # ## # ## # # # # ### # #
# # # # ### # ## # # # # ## # # # ### # ## # # # # # # # # # # # #
## # # # # ## ## # #### ## # # ## # # # # # ### # # # #### # # ## ## # # #
## # # ## # #### # # # # ## # ### ## ## #### # ## ## # ## ## #### ## ### #### #
# ### ### ### # ### ### ## # # # ###### # # ### ## # # # # # # # #
## ### ## # ## # ## # ## ### # # # #### ### ### # #
# # ## # # # ## ## # # # # ### ### # ### ### # # # # ### ## # #
## # ## # # # # ###### ## ### # ### # # # ## # ## ## # ### # # # ## # ## #
# ## # ## # # # ## ## # ## ## ## # ## # ## # # # # ## # # # #
# ### # ## ## # #### # ##### # ## ## ## ### # # # # ### #
## # ## # # # # ##### # ## # # # ## # ### # # # ## # ## # # # ## E#
####################################################################################################

View File

@ -0,0 +1,50 @@
##################################################
#S ## ### # # # #### # ## #
# # ## # # # # ##### ### # ### # #
# ## #### # ### ### ## # # #### ### # # #
#### # # # # # ## # ####
# ## # #### ## # # ## # # # #
# ### # # # # ### # ##
# # # # ## # # ## # ## # # #
## # # # #### # # # # # ### ##
## # # ## ## ### # # ## #
## # # # # # # # # # # # # #
# # ## # ## ## ## ## ## # ###
# ### # # # ## ## # # # # ## # ##
## # # # # ## # # # # #
# # # # # ### # ### # # ##
# # # # # # # ##### # ### ##
# # # # ## ## # # # # ### #### #
## # # # ## # # ## # ### ## ### # #
## # ## # # # # #
# ##### ## ## # # # ## # ## # # #
# # # # ### ##### ### # # ## #
## # # # ## # # ## # # # # ## #
#### # # ## # # # ## ## # ## ## #
# # ### ### ## # ## #### # #
# # ### # ## ##### # # # # ## # #
## #### # # # # # #
# ## # # # # ## ## # # ## # #
# # ## # ### # ### ## # ## #
# # # # # ## ## ## # #
# # ## ### ## ## # # # # # ## # #
## # # #### # #### # ## ## # ## #
# # # ## # # # # # # # # #
# ### ### # # # # # # # #
# ## # # # ####### # # # # # # ### #
## # # # # # # # # # ## # ## #
# # # # ## ## # # ## ### # # # # # # #
# ## # ### # # # # # # # #
# # # # # # # ## # ### # #
# # ### # # # ### # ## # # #
# ### # # # # ## # # ## # #
# ## # ### # ## ## ### # # # #
# ## # # ## ## # # # ## # #
# # ## # # # # # # # # ## #
## # # # # # # # # # # # #
# # # # ### ## ### # ## # # # #
## ##### # # # ## # ## ### # #
# ## # ## ##### # # # ## #
# # # # # # ### # # # # # #
### # # # # # # ## ## ### ## #E#
##################################################

View File

@ -0,0 +1,15 @@
###############
#S # #
# #
## # #
# # # #
## ## # # # #
# # # #
# # #
# # # ## #
## # # #
## #
## # # # #
# # # ##
# # #E#
###############

View File

@ -0,0 +1,21 @@
maze,strategy,time_ms,visited,path_length
small,BFS,0.1971,28,15
small,DFS,0.062,16,15
small,A*,0.1713,28,15
small,Dijkstra,0.148,28,15
medium,BFS,5.3354,1377,95
medium,DFS,0.7772,282,151
medium,A*,3.8703,500,95
medium,Dijkstra,8.3548,1363,95
large,BFS,16.9817,4391,195
large,DFS,3.414,614,285
large,A*,5.7519,559,195
large,Dijkstra,31.018,4380,195
empty,BFS,2.3012,400,39
empty,DFS,1.4237,400,191
empty,A*,3.6105,400,39
empty,Dijkstra,2.9606,400,39
no_exit,BFS,0.5791,136,0
no_exit,DFS,0.5479,136,0
no_exit,A*,0.9933,136,0
no_exit,Dijkstra,0.8121,136,0
1 maze strategy time_ms visited path_length
2 small BFS 0.1971 28 15
3 small DFS 0.062 16 15
4 small A* 0.1713 28 15
5 small Dijkstra 0.148 28 15
6 medium BFS 5.3354 1377 95
7 medium DFS 0.7772 282 151
8 medium A* 3.8703 500 95
9 medium Dijkstra 8.3548 1363 95
10 large BFS 16.9817 4391 195
11 large DFS 3.414 614 285
12 large A* 5.7519 559 195
13 large Dijkstra 31.018 4380 195
14 empty BFS 2.3012 400 39
15 empty DFS 1.4237 400 191
16 empty A* 3.6105 400 39
17 empty Dijkstra 2.9606 400 39
18 no_exit BFS 0.5791 136 0
19 no_exit DFS 0.5479 136 0
20 no_exit A* 0.9933 136 0
21 no_exit Dijkstra 0.8121 136 0

View File

@ -0,0 +1,10 @@
##########
#S #
# ###### #
# # # #
# # ## # #
# # ## # #
# # # #
# ###### #
# E#
##########

View File

@ -0,0 +1,122 @@
ОПИСАНИЕ ЗАДАЧИ И ВЫБРАННЫХ ПАТТЕРНОВ
Цель работы — разработать систему поиска пути в лабиринте с использованием
оопп и паттернов проектирования.
В работе были использованы следующие паттерны:
Strategy — для реализации алгоритмов поиска пути (BFS, DFS, A*, Dijkstra).
Позволяет менять алгоритм без изменения кода основного класса MazeSolver.
Builder — для создания лабиринта из текстового файла.
Отделяет логику загрузки данных от основной системы.
'''mermaid
classDiagram
class Cell {
+x
+y
+is_wall
+is_start
+is_exit
+weight
+isPassable()
}
class Maze {
+width
+height
+start
+exit
+getCell()
+getNeighbors()
+getWeightedNeighbors()
}
class MazeBuilder {
+buildFromFile()
}
class TextFileMazeBuilder
MazeBuilder <|-- TextFileMazeBuilder
class PathFindingStrategy {
+findPath()
}
class BFSStrategy
class DFSStrategy
class AStarStrategy
class DijkstraStrategy
PathFindingStrategy <|-- BFSStrategy
PathFindingStrategy <|-- DFSStrategy
PathFindingStrategy <|-- AStarStrategy
PathFindingStrategy <|-- DijkstraStrategy
class MazeSolver {
+setStrategy()
+solve()
}
MazeSolver --> PathFindingStrategy
Maze --> Cell
'''
ЛИСТИНГИ КЛЮЧЕВЫХ КЛАССОВ
В проекте реализованы основные классы:
Cell — хранение информации о клетке лабиринта
Maze — структура лабиринта и работа с соседями
MazeSolver — запуск поиска пути
PathFindingStrategy — интерфейс алгоритмов
BFSStrategy, DFSStrategy, AStarStrategy, DijkstraStrategy — реализации алгоритмов
TextFileMazeBuilder — загрузка лабиринта из файла
SearchStats — хранение статистики
РЕЗУЛЬТАТЫ ЭКСПЕРИМЕНТОВ
Алгоритмы тестировались на разных лабиринтах: small, medium, large, empty, no_exit.
Сравнивались:
время выполнения
количество посещённых клеток
длина найденного пути
Результаты в общем виде:
BFS — гарантирует кратчайший путь, но посещает много клеток
DFS — быстрый, но не гарантирует оптимальный путь
A* — самый быстрый в большинстве случаев за счёт эвристики
Dijkstra — стабильный, но медленнее A* на больших лабиринтах
АНАЛИЗ ЭФФЕКТИВНОСТИ И ПАТТЕРНОВ
Результаты показали, что A* является наиболее эффективным алгоритмом на больших данных,
так как использует эвристику и уменьшает количество проверяемых клеток.
BFS всегда находит оптимальный путь, но работает медленнее из-за полного обхода пространства.
DFS быстрее по времени, но не гарантирует лучший результат.
Dijkstra корректно работает с весами, но в данной задаче часто уступает A*.
Паттерн Strategy позволил легко переключать алгоритмы без изменения основной логики программы.
Паттерн Builder упростил создание лабиринтов и отделил загрузку данных от логики поиска.
ВЫВОДЫ
В ходе работы была создана гибкая система поиска пути в лабиринте с использованием ООП
и паттернов проектирования. Благодаря Strategy алгоритмы стали независимыми и легко
заменяемыми. Благодаря Builder упростилась работа с созданием и загрузкой лабиринтов.
В целом, архитектура получилась расширяемой: можно легко добавить новый алгоритм или тип
лабиринта без переписывания существующего кода.
Таким образом, наиболее сбалансированным алгоритмом для поиска пути в лабиринте является A*,
так как он обеспечивает:
высокую скорость работы,
оптимальность результата,
минимальное количество исследуемых состояний.
Алгоритмы BFS и Dijkstra гарантируют оптимальность, но проигрывают по производительности,
а DFS является самым быстрым, но не гарантирует качество решения.

789
stepinim/lab2_oop/poisk.py Normal file
View File

@ -0,0 +1,789 @@
import time
from collections import deque
import heapq
import csv
import os
import random
import matplotlib.pyplot as plt
# ============================================================
# ЭТАП 1. МОДЕЛЬ ЛАБИРИНТА
# ============================================================
class Cell:
def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_start = is_start
self.is_exit = is_exit
self.weight = 1
def isPassable(self):
return not self.is_wall
def __repr__(self):
return f"Cell({self.x},{self.y})"
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return isinstance(other, Cell) and self.x == other.x and self.y == other.y
class Maze:
def __init__(self, width, height):
self.width = width
self.height = height
self.cells = []
self.start = None
self.exit = None
def getCell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[y][x]
return None
def getNeighbors(self, cell):
neighbors = []
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
nx = cell.x + dx
ny = cell.y + dy
neighbor = self.getCell(nx, ny)
if neighbor and neighbor.isPassable():
neighbors.append(neighbor)
return neighbors
def getWeightedNeighbors(self, cell):
return [(n, n.weight) for n in self.getNeighbors(cell)]
# ============================================================
# ЭТАП 2. BUILDER
# ============================================================
class MazeBuilder:
def buildFromFile(self, filename):
raise NotImplementedError
class TextFileMazeBuilder(MazeBuilder):
def buildFromFile(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = [line.rstrip('\n') for line in f]
height = len(lines)
width = max(len(line) for line in lines)
maze = Maze(width, height)
for y, line in enumerate(lines):
row = []
for x, char in enumerate(line):
if char == '#':
cell = Cell(x, y, is_wall=True)
elif char == 'S':
cell = Cell(x, y, is_start=True)
maze.start = cell
elif char == 'E':
cell = Cell(x, y, is_exit=True)
maze.exit = cell
else:
cell = Cell(x, y)
row.append(cell)
while len(row) < width:
row.append(Cell(len(row), y, is_wall=True))
maze.cells.append(row)
if maze.start is None or maze.exit is None:
raise ValueError("В лабиринте нет S или E")
return maze
# ============================================================
# ВОССТАНОВЛЕНИЕ ПУТИ
# ============================================================
def reconstruct_path(parents, end_cell):
path = []
current = end_cell
while current is not None:
path.append(current)
current = parents[current]
path.reverse()
return path
# ============================================================
# ЭТАП 3. STRATEGY
# ============================================================
class PathFindingStrategy:
@property
def name(self):
return "Unknown"
def findPath(self, maze, start, exit):
raise NotImplementedError
# ============================================================
# BFS
# ============================================================
class BFSStrategy(PathFindingStrategy):
@property
def name(self):
return "BFS"
def findPath(self, maze, start, exit):
queue = deque([start])
visited = {start}
parents = {
start: None
}
visited_count = 1
while queue:
current = queue.popleft()
if current == exit:
path = reconstruct_path(parents, exit)
return path, visited_count
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parents[neighbor] = current
visited_count += 1
queue.append(neighbor)
return [], visited_count
# ============================================================
# DFS
# ============================================================
class DFSStrategy(PathFindingStrategy):
@property
def name(self):
return "DFS"
def findPath(self, maze, start, exit):
stack = [start]
visited = {start}
parents = {
start: None
}
visited_count = 1
while stack:
current = stack.pop()
if current == exit:
path = reconstruct_path(parents, exit)
return path, visited_count
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parents[neighbor] = current
visited_count += 1
stack.append(neighbor)
return [], visited_count
# ============================================================
# A*
# ============================================================
class AStarStrategy(PathFindingStrategy):
@property
def name(self):
return "A*"
def heuristic(self, a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def findPath(self, maze, start, exit):
counter = 0
open_set = []
heapq.heappush(open_set, (0, counter, start))
parents = {
start: None
}
g_score = {
start: 0
}
visited = set()
visited_count = 0
while open_set:
_, _, current = heapq.heappop(open_set)
if current in visited:
continue
visited.add(current)
visited_count += 1
if current == exit:
path = reconstruct_path(parents, exit)
return path, visited_count
for neighbor in maze.getNeighbors(current):
tentative_g = g_score[current] + 1
if neighbor not in g_score or tentative_g < g_score[neighbor]:
g_score[neighbor] = tentative_g
parents[neighbor] = current
f_score = tentative_g + self.heuristic(neighbor, exit)
counter += 1
heapq.heappush(
open_set,
(f_score, counter, neighbor)
)
return [], visited_count
# ============================================================
# DIJKSTRA
# ============================================================
class DijkstraStrategy(PathFindingStrategy):
@property
def name(self):
return "Dijkstra"
def findPath(self, maze, start, exit):
counter = 0
queue = []
heapq.heappush(queue, (0, counter, start))
distances = {
start: 0
}
parents = {
start: None
}
visited = set()
visited_count = 0
while queue:
dist, _, current = heapq.heappop(queue)
if current in visited:
continue
visited.add(current)
visited_count += 1
if current == exit:
path = reconstruct_path(parents, exit)
return path, visited_count
for neighbor, weight in maze.getWeightedNeighbors(current):
new_dist = dist + weight
if neighbor not in distances or new_dist < distances[neighbor]:
distances[neighbor] = new_dist
parents[neighbor] = current
counter += 1
heapq.heappush(
queue,
(new_dist, counter, neighbor)
)
return [], visited_count
# ============================================================
# ЭТАП 4. STATS + SOLVER
# ============================================================
class SearchStats:
def __init__(
self,
strategy_name,
time_ms,
visited_cells,
path_length,
path_found
):
self.strategy_name = strategy_name
self.time_ms = time_ms
self.visited_cells = visited_cells
self.path_length = path_length
self.path_found = path_found
class MazeSolver:
def __init__(self, maze, strategy=None):
self.maze = maze
self.strategy = strategy
def setStrategy(self, strategy):
self.strategy = strategy
def solve(self):
if self.strategy is None:
raise ValueError("Стратегия не выбрана")
start_time = time.perf_counter()
path, visited = self.strategy.findPath(
self.maze,
self.maze.start,
self.maze.exit
)
end_time = time.perf_counter()
elapsed_ms = (end_time - start_time) * 1000
return SearchStats(
self.strategy.name,
elapsed_ms,
visited,
len(path),
len(path) > 0
), path
# ============================================================
# ВИЗУАЛИЗАЦИЯ
# ============================================================
def render(maze, path=None):
path_set = set(path) if path else set()
for y in range(maze.height):
line = ""
for x in range(maze.width):
cell = maze.getCell(x, y)
if cell == maze.start:
line += "S"
elif cell == maze.exit:
line += "E"
elif cell in path_set:
line += "."
elif cell.is_wall:
line += "#"
else:
line += " "
print(line)
print()
# ============================================================
# ФАЙЛЫ И ПУТИ
# ============================================================
OUTPUT_DIR = os.path.join("docs", "data")
PREFIX = "_2lab"
os.makedirs(OUTPUT_DIR, exist_ok=True)
def get_path(filename):
name, ext = os.path.splitext(filename)
return os.path.join(
OUTPUT_DIR,
f"{name}{PREFIX}{ext}"
)
# ============================================================
# СОЗДАНИЕ ЛАБИРИНТА
# ============================================================
def create_test_maze(filename, lines):
with open(filename, 'w', encoding='utf-8') as f:
for line in lines:
f.write(line + '\n')
return filename
# ============================================================
# ГЕНЕРАЦИЯ
# ============================================================
def generate_maze(width, height, wall_density=0.3):
grid = [[' ' for _ in range(width)] for _ in range(height)]
for x in range(width):
grid[0][x] = '#'
grid[height - 1][x] = '#'
for y in range(height):
grid[y][0] = '#'
grid[y][width - 1] = '#'
x, y = 1, 1
path_cells = {(x, y)}
while x < width - 2 or y < height - 2:
if x < width - 2 and random.random() > 0.3:
x += 1
elif y < height - 2:
y += 1
else:
x += 1
path_cells.add((x, y))
for yy in range(1, height - 1):
for xx in range(1, width - 1):
if (xx, yy) not in path_cells:
if random.random() < wall_density:
grid[yy][xx] = '#'
grid[1][1] = 'S'
grid[height - 2][width - 2] = 'E'
return [''.join(row) for row in grid]
def generate_empty_maze(size):
lines = [" " * size for _ in range(size)]
lines[0] = "S" + " " * (size - 1)
lines[size - 1] = " " * (size - 1) + "E"
return lines
def generate_no_exit_maze(size):
lines = generate_maze(size, size, wall_density=0.2)
for y, line in enumerate(lines):
if 'E' in line:
x = line.index('E')
for dy, dx in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
ny = y + dy
nx = x + dx
if 0 <= ny < size and 0 <= nx < size:
if lines[ny][nx] == ' ':
lines[ny] = (
lines[ny][:nx]
+ '#'
+ lines[ny][nx + 1:]
)
return lines
# ============================================================
# ЭКСПЕРИМЕНТЫ
# ============================================================
def run_experiments():
mazes = {
"small": [
"##########",
"#S #",
"# ###### #",
"# # # #",
"# # ## # #",
"# # ## # #",
"# # # #",
"# ###### #",
"# E#",
"##########"
],
"medium": generate_maze(50, 50, 0.35),
"large": generate_maze(100, 100, 0.4),
"empty": generate_empty_maze(20),
"no_exit": generate_no_exit_maze(15)
}
strategies = [
BFSStrategy(),
DFSStrategy(),
AStarStrategy(),
DijkstraStrategy()
]
results = []
print("=" * 60)
print("ЭКСПЕРИМЕНТЫ")
print("=" * 60)
for maze_name, lines in mazes.items():
filename = get_path(f"{maze_name}.txt")
create_test_maze(filename, lines)
maze = TextFileMazeBuilder().buildFromFile(filename)
print(f"\nЛабиринт: {maze_name}")
print("-" * 60)
for strategy in strategies:
times = []
visited_values = []
final_path_len = 0
for _ in range(5):
solver = MazeSolver(maze)
solver.setStrategy(strategy)
stats, path = solver.solve()
times.append(stats.time_ms)
visited_values.append(stats.visited_cells)
final_path_len = stats.path_length
avg_time = sum(times) / len(times)
avg_visited = sum(visited_values) / len(visited_values)
results.append({
"maze": maze_name,
"strategy": strategy.name,
"time_ms": round(avg_time, 4),
"visited": int(avg_visited),
"path_length": final_path_len
})
status = "найден" if final_path_len > 0 else "не найден"
print(
f"{strategy.name:<10} | "
f"{avg_time:>8.4f} мс | "
f"{int(avg_visited):>5} клеток | "
f"путь {status}"
)
csv_path = get_path("results.csv")
with open(csv_path, "w", newline="", encoding='utf-8') as f:
writer = csv.DictWriter(
f,
fieldnames=[
"maze",
"strategy",
"time_ms",
"visited",
"path_length"
]
)
writer.writeheader()
writer.writerows(results)
print(f"\nCSV сохранён: {csv_path}")
return results
# ============================================================
# ГРАФИК
# ============================================================
def build_charts(results):
mazes = list(dict.fromkeys(r["maze"] for r in results))
strategies = list(dict.fromkeys(r["strategy"] for r in results))
fig, ax = plt.subplots(figsize=(12, 6))
x = range(len(mazes))
width = 0.2
colors = {
'BFS': '#3498db',
'DFS': '#e74c3c',
'A*': '#2ecc71',
'Dijkstra': '#f39c12'
}
for i, strategy in enumerate(strategies):
times = [
r["time_ms"]
for r in results
if r["strategy"] == strategy
]
ax.bar(
[j + i * width for j in x],
times,
width,
label=strategy,
color=colors.get(strategy, 'gray')
)
ax.set_xlabel("Лабиринт")
ax.set_ylabel("Время (мс)")
ax.set_title("Сравнение алгоритмов")
ax.set_xticks([j + width * 1.5 for j in x])
ax.set_xticklabels(mazes)
ax.legend()
ax.grid(axis='y', alpha=0.3)
plt.tight_layout()
chart_path = get_path("chart_time.png")
plt.savefig(chart_path, dpi=150, bbox_inches='tight')
print(f"График сохранён: {chart_path}")
plt.show()
# ============================================================
# MAIN
# ============================================================
def main():
results = run_experiments()
build_charts(results)
if __name__ == "__main__":
main()