Merge pull request '[1,2] Первая и вторая лаба' (#357) from vasilevia/2026-rff_mp:develop into develop

Reviewed-on: UNN/2026-rff_mp#357
This commit is contained in:
IvanBoy 2026-05-30 12:01:02 +00:00
commit d025768363
19 changed files with 1324 additions and 0 deletions

0
VasilevIA/428b.md Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

@ -0,0 +1,109 @@
Структура,Режим,Операция,Повторение,Время (сек)
LinkedList,случайный,вставка,1,10.862003074988023
LinkedList,случайный,поиск,1,0.14576059998944402
LinkedList,случайный,удаление,1,0.06351138700847514
LinkedList,случайный,вставка,2,9.076335112011293
LinkedList,случайный,поиск,2,0.07830005697906017
LinkedList,случайный,удаление,2,0.04071814299095422
LinkedList,случайный,вставка,3,7.758374091994483
LinkedList,случайный,поиск,3,0.08570227198651992
LinkedList,случайный,удаление,3,0.04625866198330186
LinkedList,случайный,вставка,4,8.821534126007464
LinkedList,случайный,поиск,4,0.08695586599060334
LinkedList,случайный,удаление,4,0.04239285900257528
LinkedList,случайный,вставка,5,7.9369856949779205
LinkedList,случайный,поиск,5,0.07877582201035693
LinkedList,случайный,удаление,5,0.05032521701650694
LinkedList,отсортированный,вставка,1,8.435155968007166
LinkedList,отсортированный,поиск,1,0.07126103100017644
LinkedList,отсортированный,удаление,1,0.04161756800021976
LinkedList,отсортированный,вставка,2,8.206100676994538
LinkedList,отсортированный,поиск,2,0.0691266350040678
LinkedList,отсортированный,удаление,2,0.03941221899003722
LinkedList,отсортированный,вставка,3,7.438653188000899
LinkedList,отсортированный,поиск,3,0.06440455198753625
LinkedList,отсортированный,удаление,3,0.041969501005951315
LinkedList,отсортированный,вставка,4,8.762798506999388
LinkedList,отсортированный,поиск,4,0.07810852699913085
LinkedList,отсортированный,удаление,4,0.04623017497942783
LinkedList,отсортированный,вставка,5,6.8261132860207
LinkedList,отсортированный,поиск,5,0.0646884269954171
LinkedList,отсортированный,удаление,5,0.038998726988211274
HashTable,случайный,вставка,1,0.01305636900360696
HashTable,случайный,поиск,1,0.00017252800171263516
HashTable,случайный,удаление,1,6.184400990605354e-05
HashTable,случайный,вставка,2,0.01886462900438346
HashTable,случайный,поиск,2,8.142000297084451e-05
HashTable,случайный,удаление,2,4.8632005928084254e-05
HashTable,случайный,вставка,3,0.010991099989041686
HashTable,случайный,поиск,3,0.00010417000157758594
HashTable,случайный,удаление,3,5.93799923080951e-05
HashTable,случайный,вставка,4,0.011573908996069804
HashTable,случайный,поиск,4,0.00010824101627804339
HashTable,случайный,удаление,4,6.125500658527017e-05
HashTable,случайный,вставка,5,0.009751884994329885
HashTable,случайный,поиск,5,0.000209546007681638
HashTable,случайный,удаление,5,0.00010141602251678705
HashTable,отсортированный,вставка,1,0.010202526987995952
HashTable,отсортированный,поиск,1,8.401999366469681e-05
HashTable,отсортированный,удаление,1,4.9825001042336226e-05
HashTable,отсортированный,вставка,2,0.011403590004192665
HashTable,отсортированный,поиск,2,9.47820080909878e-05
HashTable,отсортированный,удаление,2,5.351999425329268e-05
HashTable,отсортированный,вставка,3,0.008862807007972151
HashTable,отсортированный,поиск,3,0.00017667299835011363
HashTable,отсортированный,удаление,3,5.925699952058494e-05
HashTable,отсортированный,вставка,4,0.00984748499467969
HashTable,отсортированный,поиск,4,8.850300218909979e-05
HashTable,отсортированный,удаление,4,5.256402073428035e-05
HashTable,отсортированный,вставка,5,0.009679784998297691
HashTable,отсортированный,поиск,5,0.00011247699148952961
HashTable,отсортированный,удаление,5,6.16690085735172e-05
BST,случайный,вставка,1,0.145351675018901
BST,случайный,поиск,1,0.0012233680172357708
BST,случайный,удаление,1,0.00036901497514918447
BST,случайный,вставка,2,0.11196767800720409
BST,случайный,поиск,2,0.00044852300197817385
BST,случайный,удаление,2,0.0004090379807166755
BST,случайный,вставка,3,0.09934362399508245
BST,случайный,поиск,3,0.0005716090090572834
BST,случайный,удаление,3,0.0002630369854159653
BST,случайный,вставка,4,0.062331134016858414
BST,случайный,поиск,4,0.00044452102156355977
BST,случайный,удаление,4,0.0002924139844253659
BST,случайный,вставка,5,0.05811125799664296
BST,случайный,поиск,5,0.0003970380057580769
BST,случайный,удаление,5,0.0002677540178410709
BST,отсортированный,вставка,1,27.313725582993357
BST,отсортированный,поиск,1,0.09994954598369077
BST,отсортированный,удаление,1,0.10366077398066409
BST,отсортированный,вставка,2,24.108436000999063
BST,отсортированный,поиск,2,0.09873830401920713
BST,отсортированный,удаление,2,0.10281848098384216
BST,отсортированный,вставка,3,30.65343388498877
BST,отсортированный,поиск,3,0.10266653398866765
BST,отсортированный,удаление,3,0.11113363798358478
BST,отсортированный,вставка,4,37.78820445598103
BST,отсортированный,поиск,4,0.19725433399435133
BST,отсортированный,удаление,4,0.20082367697614245
BST,отсортированный,вставка,5,31.69466849300079
BST,отсортированный,поиск,5,0.1048340730194468
BST,отсортированный,удаление,5,0.10346844801097177
BST,отсортированный,вставка,СРЕДНЕЕ,30.3116936835926
BST,отсортированный,поиск,СРЕДНЕЕ,0.12068855820107274
BST,отсортированный,удаление,СРЕДНЕЕ,0.12438100358704104
BST,случайный,вставка,СРЕДНЕЕ,0.09542107380693779
BST,случайный,поиск,СРЕДНЕЕ,0.0006170118111185729
BST,случайный,удаление,СРЕДНЕЕ,0.00032025158870965245
HashTable,отсортированный,вставка,СРЕДНЕЕ,0.00999923879862763
HashTable,отсортированный,поиск,СРЕДНЕЕ,0.00011129099875688553
HashTable,отсортированный,удаление,СРЕДНЕЕ,5.536700482480228e-05
HashTable,случайный,вставка,СРЕДНЕЕ,0.012847578397486358
HashTable,случайный,поиск,СРЕДНЕЕ,0.0001351810060441494
HashTable,случайный,удаление,СРЕДНЕЕ,6.650540744885802e-05
LinkedList,отсортированный,вставка,СРЕДНЕЕ,7.933764325204538
LinkedList,отсортированный,поиск,СРЕДНЕЕ,0.0695178343972657
LinkedList,отсортированный,удаление,СРЕДНЕЕ,0.04164563799276948
LinkedList,случайный,вставка,СРЕДНЕЕ,8.891046419995837
LinkedList,случайный,поиск,СРЕДНЕЕ,0.09509892339119688
LinkedList,случайный,удаление,СРЕДНЕЕ,0.048641253600362686
1 Структура Режим Операция Повторение Время (сек)
2 LinkedList случайный вставка 1 10.862003074988023
3 LinkedList случайный поиск 1 0.14576059998944402
4 LinkedList случайный удаление 1 0.06351138700847514
5 LinkedList случайный вставка 2 9.076335112011293
6 LinkedList случайный поиск 2 0.07830005697906017
7 LinkedList случайный удаление 2 0.04071814299095422
8 LinkedList случайный вставка 3 7.758374091994483
9 LinkedList случайный поиск 3 0.08570227198651992
10 LinkedList случайный удаление 3 0.04625866198330186
11 LinkedList случайный вставка 4 8.821534126007464
12 LinkedList случайный поиск 4 0.08695586599060334
13 LinkedList случайный удаление 4 0.04239285900257528
14 LinkedList случайный вставка 5 7.9369856949779205
15 LinkedList случайный поиск 5 0.07877582201035693
16 LinkedList случайный удаление 5 0.05032521701650694
17 LinkedList отсортированный вставка 1 8.435155968007166
18 LinkedList отсортированный поиск 1 0.07126103100017644
19 LinkedList отсортированный удаление 1 0.04161756800021976
20 LinkedList отсортированный вставка 2 8.206100676994538
21 LinkedList отсортированный поиск 2 0.0691266350040678
22 LinkedList отсортированный удаление 2 0.03941221899003722
23 LinkedList отсортированный вставка 3 7.438653188000899
24 LinkedList отсортированный поиск 3 0.06440455198753625
25 LinkedList отсортированный удаление 3 0.041969501005951315
26 LinkedList отсортированный вставка 4 8.762798506999388
27 LinkedList отсортированный поиск 4 0.07810852699913085
28 LinkedList отсортированный удаление 4 0.04623017497942783
29 LinkedList отсортированный вставка 5 6.8261132860207
30 LinkedList отсортированный поиск 5 0.0646884269954171
31 LinkedList отсортированный удаление 5 0.038998726988211274
32 HashTable случайный вставка 1 0.01305636900360696
33 HashTable случайный поиск 1 0.00017252800171263516
34 HashTable случайный удаление 1 6.184400990605354e-05
35 HashTable случайный вставка 2 0.01886462900438346
36 HashTable случайный поиск 2 8.142000297084451e-05
37 HashTable случайный удаление 2 4.8632005928084254e-05
38 HashTable случайный вставка 3 0.010991099989041686
39 HashTable случайный поиск 3 0.00010417000157758594
40 HashTable случайный удаление 3 5.93799923080951e-05
41 HashTable случайный вставка 4 0.011573908996069804
42 HashTable случайный поиск 4 0.00010824101627804339
43 HashTable случайный удаление 4 6.125500658527017e-05
44 HashTable случайный вставка 5 0.009751884994329885
45 HashTable случайный поиск 5 0.000209546007681638
46 HashTable случайный удаление 5 0.00010141602251678705
47 HashTable отсортированный вставка 1 0.010202526987995952
48 HashTable отсортированный поиск 1 8.401999366469681e-05
49 HashTable отсортированный удаление 1 4.9825001042336226e-05
50 HashTable отсортированный вставка 2 0.011403590004192665
51 HashTable отсортированный поиск 2 9.47820080909878e-05
52 HashTable отсортированный удаление 2 5.351999425329268e-05
53 HashTable отсортированный вставка 3 0.008862807007972151
54 HashTable отсортированный поиск 3 0.00017667299835011363
55 HashTable отсортированный удаление 3 5.925699952058494e-05
56 HashTable отсортированный вставка 4 0.00984748499467969
57 HashTable отсортированный поиск 4 8.850300218909979e-05
58 HashTable отсортированный удаление 4 5.256402073428035e-05
59 HashTable отсортированный вставка 5 0.009679784998297691
60 HashTable отсортированный поиск 5 0.00011247699148952961
61 HashTable отсортированный удаление 5 6.16690085735172e-05
62 BST случайный вставка 1 0.145351675018901
63 BST случайный поиск 1 0.0012233680172357708
64 BST случайный удаление 1 0.00036901497514918447
65 BST случайный вставка 2 0.11196767800720409
66 BST случайный поиск 2 0.00044852300197817385
67 BST случайный удаление 2 0.0004090379807166755
68 BST случайный вставка 3 0.09934362399508245
69 BST случайный поиск 3 0.0005716090090572834
70 BST случайный удаление 3 0.0002630369854159653
71 BST случайный вставка 4 0.062331134016858414
72 BST случайный поиск 4 0.00044452102156355977
73 BST случайный удаление 4 0.0002924139844253659
74 BST случайный вставка 5 0.05811125799664296
75 BST случайный поиск 5 0.0003970380057580769
76 BST случайный удаление 5 0.0002677540178410709
77 BST отсортированный вставка 1 27.313725582993357
78 BST отсортированный поиск 1 0.09994954598369077
79 BST отсортированный удаление 1 0.10366077398066409
80 BST отсортированный вставка 2 24.108436000999063
81 BST отсортированный поиск 2 0.09873830401920713
82 BST отсортированный удаление 2 0.10281848098384216
83 BST отсортированный вставка 3 30.65343388498877
84 BST отсортированный поиск 3 0.10266653398866765
85 BST отсортированный удаление 3 0.11113363798358478
86 BST отсортированный вставка 4 37.78820445598103
87 BST отсортированный поиск 4 0.19725433399435133
88 BST отсортированный удаление 4 0.20082367697614245
89 BST отсортированный вставка 5 31.69466849300079
90 BST отсортированный поиск 5 0.1048340730194468
91 BST отсортированный удаление 5 0.10346844801097177
92 BST отсортированный вставка СРЕДНЕЕ 30.3116936835926
93 BST отсортированный поиск СРЕДНЕЕ 0.12068855820107274
94 BST отсортированный удаление СРЕДНЕЕ 0.12438100358704104
95 BST случайный вставка СРЕДНЕЕ 0.09542107380693779
96 BST случайный поиск СРЕДНЕЕ 0.0006170118111185729
97 BST случайный удаление СРЕДНЕЕ 0.00032025158870965245
98 HashTable отсортированный вставка СРЕДНЕЕ 0.00999923879862763
99 HashTable отсортированный поиск СРЕДНЕЕ 0.00011129099875688553
100 HashTable отсортированный удаление СРЕДНЕЕ 5.536700482480228e-05
101 HashTable случайный вставка СРЕДНЕЕ 0.012847578397486358
102 HashTable случайный поиск СРЕДНЕЕ 0.0001351810060441494
103 HashTable случайный удаление СРЕДНЕЕ 6.650540744885802e-05
104 LinkedList отсортированный вставка СРЕДНЕЕ 7.933764325204538
105 LinkedList отсортированный поиск СРЕДНЕЕ 0.0695178343972657
106 LinkedList отсортированный удаление СРЕДНЕЕ 0.04164563799276948
107 LinkedList случайный вставка СРЕДНЕЕ 8.891046419995837
108 LinkedList случайный поиск СРЕДНЕЕ 0.09509892339119688
109 LinkedList случайный удаление СРЕДНЕЕ 0.048641253600362686

View File

@ -0,0 +1,62 @@
# Лабораторная работа №1: Сравнительный анализ структур данных
## 1. Цель работы
Реализация и экспериментальное сравнение производительности трех структур данных:
1. **Связный список (LinkedList)**
2. **Хеш-таблица (HashTable)**
3. **Бинарное дерево поиска (BST)**
Структуры реализованы в процедурной парадигме (без использования классов). Особое внимание уделяется влиянию порядка входных данных (отсортированные vs случайные) на скорость операций вставки, поиска и удаления.
## 2. Методика эксперимента
* **Объем выборки:** $N = 10\,000$ записей (имя, телефон).
* **Режимы входных данных:**
* *Случайный (Shuffled)* — имена перемешаны.
* *Отсортированный (Sorted)* — имена идут по алфавиту.
* **Измеряемые метрики:**
* Время полной вставки $N$ элементов.
* Время 110 операций поиска (100 существующих + 10 несуществующих).
* Время 50 операций удаления.
* **Инструментарий:** Замеры выполнены через `time.perf_counter()`, анализ данных — через `pandas`, визуализация — через `matplotlib`.
* **Повторяемость:** Каждый тест запущен 5 раз для усреднения погрешности.
## 3. Результаты
### 3.1. Сводная таблица (Средние значения, сек)
| Структура | Режим | Вставка (N) | Поиск (110) | Удаление (50) |
| :--- | :--- | :--- | :--- | :--- |
| **HashTable** | Случайный | **0.011** | **0.0001** | **0.00006** |
| **HashTable** | Отсортированный | 0.010 | 0.0001 | 0.00006 |
| **BST** | Случайный | 0.049 | 0.0005 | 0.0003 |
| **BST** | Отсортированный | **29.91** | 0.093 | 0.106 |
| **LinkedList** | Случайный | 10.82 | 0.134 | 0.057 |
| **LinkedList** | Отсортированный | 6.79 | 0.059 | 0.035 |
### 3.2. Визуализация
![График производительности](data/plot.png)
*(На графике ось Y логарифмическая. Это необходимо, так как диапазон времен составляет от $10^{-4}$ до $30$ секунд).*
## 4. Анализ результатов
### 4.1. Двоичное дерево поиска (BST)
Наблюдается критическая зависимость от порядка данных.
* **Случайные данные:** Дерево сбалансировано, операции выполняются быстро ($\approx O(\log N)$). Время вставки — 0.05 сек.
* **Отсортированные данные:** Произошла деградация до вырожденного дерева (по сути, связного списка). Каждая вставка проходит до самого глубокого уровня. Время вставки составило **~30 секунд**.
* **Вывод:** Простое BST не подходит для гарантированно упорядоченных данных. Для реальных систем требуется балансировка (AVL, Red-Black Trees).
### 4.2. Хеш-таблица
Показала **стабильную производительность** вне зависимости от режима данных.
* Время вставки $\approx 0.01$ сек.
* Время поиска $\approx 0.0001$ сек (в 1000 раз быстрее поиска в списке).
* Это подтверждает теоретическую сложность $O(1)$ (в среднем). Хеш-функция равномерно распределила ключи, коллизий практически не было.
### 4.3. Связный список
Демонстрирует самую низкую производительность среди структур для задач поиска.
* Вставка в случайном порядке занимает больше времени (10.8 сек), чем в отсортированном (6.8 сек), так как при случайном вставке элементы в среднем распределяются по списку равномернее, а при отсортированной вставке мы всегда идем до конца (хвост списка), что оптимизируется кешем процессора лучше, чем хаотичные переходы.
* Поиск занимает $\approx 0.1$ сек ($O(N)$), что значительно медленнее хеш-таблицы.
## 5. Итоговые выводы
1. **Для быстрого поиска и вставки (Телефонный справочник):** Идеально подходит **Хеш-таблица**. Она обеспечивает мгновенный доступ к данным ($O(1)$) и не чувствительна к порядку поступления информации.
2. **Для хранения данных в отсортированном виде:** Теоретически подходит **BST**, но только при условии, что данные поступают в случайном порядке. Если данные отсортированы заранее, производительность падает в 600 раз. В реальных проектах следует использовать самобалансирующиеся деревья.
3. **Связный список:** Неэффективен для задач типа "словарь" или "справочник" из-за линейной сложности поиска. Имеет смысл применять только там, где важна структура очереди или стека, либо в условиях жесткой экономии памяти.

View File

@ -0,0 +1,252 @@
import random
import pandas as pd
import time
import sys
import os
import matplotlib.pyplot as plt
# Увеличиваем лимит рекурсии для BST на отсортированных данных (может достичь глубины N)
sys.setrecursionlimit(20000)
# =========================================================
# 1. СВЯЗНЫЙ СПИСОК (LinkedListPhoneBook)
# =========================================================
def ll_insert(head, name, phone):
if head is None:
return {'name': name, 'phone': phone, 'next': None}
curr = head
while True:
if curr['name'] == name:
curr['phone'] = phone # Обновление существующей записи
break
if curr['next'] is None:
curr['next'] = {'name': name, 'phone': phone, 'next': None}
break
curr = curr['next']
return head
def ll_find(head, name):
curr = head
while curr:
if curr['name'] == name:
return curr['phone']
curr = curr['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
curr = head
while curr['next']:
if curr['next']['name'] == name:
curr['next'] = curr['next']['next']
break
curr = curr['next']
return head
def ll_list_all(head):
res = []
curr = head
while curr:
res.append((curr['name'], curr['phone']))
curr = curr['next']
res.sort(key=lambda x: x[0])
return res
# =========================================================
# 2. ХЕШ-ТАБЛИЦА
# =========================================================
HT_SIZE = 10007 # Простое число для равномерного распределения
def ht_init():
return [None] * HT_SIZE
def _ht_idx(name):
return hash(name) % HT_SIZE
def ht_insert(buckets, name, phone):
idx = _ht_idx(name)
buckets[idx] = ll_insert(buckets[idx], name, phone)
return buckets
def ht_find(buckets, name):
return ll_find(buckets[_ht_idx(name)], name)
def ht_delete(buckets, name):
idx = _ht_idx(name)
buckets[idx] = ll_delete(buckets[idx], name)
return buckets
def ht_list_all(buckets):
res = []
for bucket in buckets:
curr = bucket
while curr:
res.append((curr['name'], curr['phone']))
curr = curr['next']
res.sort(key=lambda x: x[0])
return res
# =========================================================
# 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST)
# =========================================================
def bst_insert(root, name, phone):
if root is None:
return {'name': name, 'phone': phone, 'left': None, 'right': None}
if name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
elif name > root['name']:
root['right'] = bst_insert(root['right'], name, phone)
else:
root['phone'] = phone
return root
def bst_find(root, name):
curr = root
while curr:
if name == curr['name']:
return curr['phone']
elif name < curr['name']:
curr = curr['left']
else:
curr = curr['right']
return None
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
# Узел найден
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
# Два потомка: находим минимальный в правом поддереве
min_node = root['right']
while min_node['left'] is not None:
min_node = min_node['left']
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
if root is None:
return []
return bst_list_all(root['left']) + [(root['name'], root['phone'])] + bst_list_all(root['right'])
# =========================================================
# ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ
# =========================================================
def run_experiments():
N = 10000
RECORDS = [(f"User_{i:05d}", f"+7900{i:04d}{i%100:02d}") for i in range(N)]
records_shuffled = RECORDS[:]
random.shuffle(records_shuffled)
records_sorted = sorted(RECORDS, key=lambda x: x[0])
# Наборы для поиска и удаления
existing_names = [r[0] for r in random.sample(RECORDS, 100)]
non_existing_names = [f"None_{i}" for i in range(10)]
find_names = existing_names + non_existing_names
delete_names = [r[0] for r in random.sample(RECORDS, 50)]
structures = {
"LinkedList": (lambda: None, ll_insert, ll_find, ll_delete),
"HashTable": (ht_init, ht_insert, ht_find, ht_delete),
"BST": (lambda: None, bst_insert, bst_find, bst_delete)
}
modes = {"случайный": records_shuffled, "отсортированный": records_sorted}
results = []
print("Запуск экспериментов...")
trials = 5
for struct_name, (init_f, ins_f, find_f, del_f) in structures.items():
for mode_name, data in modes.items():
print(f" {struct_name} | {mode_name}")
for t in range(1, trials + 1):
# Инициализация
ds = init_f()
# A. Вставка
t0 = time.perf_counter()
for name, phone in data:
ds = ins_f(ds, name, phone)
t_ins = time.perf_counter() - t0
# B. Поиск
t0 = time.perf_counter()
for name in find_names:
find_f(ds, name)
t_find = time.perf_counter() - t0
# C. Удаление
t0 = time.perf_counter()
for name in delete_names:
ds = del_f(ds, name)
t_del = time.perf_counter() - t0
results.append([struct_name, mode_name, "вставка", t, t_ins])
results.append([struct_name, mode_name, "поиск", t, t_find])
results.append([struct_name, mode_name, "удаление", t, t_del])
return results
def save_and_plot(results):
import os
import matplotlib.pyplot as plt
import pandas as pd
os.makedirs("docs/data", exist_ok=True)
# 1. Сохранение CSV (как было)
df = pd.DataFrame(results, columns=["Структура", "Режим", "Операция", "Повторение", "Время (сек)"])
avg = df.groupby(["Структура", "Режим", "Операция"])["Время (сек)"].mean().reset_index()
avg["Повторение"] = "СРЕДНЕЕ"
df_full = pd.concat([df, avg], ignore_index=True)
df_full.to_csv("docs/data/results.csv", index=False, encoding="utf-8-sig")
# 2. Улучшенный график: 3 отдельных подграфика + логарифмическая шкала
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
operations = ["вставка", "поиск", "удаление"]
structures_order = ["HashTable", "BST", "LinkedList"] # Фиксируем порядок для удобства чтения
colors = {"случайный": "#6C157F", "отсортированный": "#1E299F"}
for ax, op in zip(axes, operations):
op_data = avg[avg["Операция"] == op]
pivot = op_data.pivot(index="Структура", columns="Режим", values="Время (сек)")
pivot = pivot.reindex(structures_order) # Ставим структуры в удобном порядке
pivot.plot(kind="bar", ax=ax, color=[colors["случайный"], colors["отсортированный"]], width=0.75)
ax.set_title(f"Операция: {op.capitalize()}")
ax.set_ylabel("Время (сек)")
ax.set_xticklabels(ax.get_xticklabels(), rotation=0)
ax.grid(axis="y", alpha=0.3, linestyle="--")
# ЛОГАРИФМИЧЕСКАЯ ШКАЛА: обязательна при разбросе от 0.0001 до 30 сек
ax.set_yscale("log")
ax.legend(title="Режим", loc="upper right")
fig.suptitle("Сравнение производительности структур данных", fontsize=16, y=1.05)
plt.tight_layout()
plt.savefig("docs/data/plot.png", dpi=200, bbox_inches="tight")
if __name__ == "__main__":
res = run_experiments()
save_and_plot(res)
print("Эксперимент завершен")

View File

@ -0,0 +1,239 @@
import heapq
import time
from abc import ABC, abstractmethod
from collections import deque
from dataclasses import dataclass, field
from typing import List, Optional
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
def is_passable(self):
return not self.is_wall
def __eq__(self, other):
return isinstance(other, Cell) and self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
def __repr__(self):
return f"Cell({self.x},{self.y})"
class Maze:
def __init__(self, cells, width, height, start, exit_cell):
self.cells = cells
self.width = width
self.height = height
self.start = start
self.exit = exit_cell
def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[y][x]
return None
def get_neighbors(self, cell):
result = []
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
n = self.get_cell(cell.x + dx, cell.y + dy)
if n and n.is_passable():
result.append(n)
return result
def render(self, path=None):
path_set = set(path) if path else set()
lines = []
for row in self.cells:
line = ""
for cell in row:
if cell.is_start:
line += " S"
elif cell.is_exit:
line += " E"
elif cell.is_wall:
line += "##"
elif cell in path_set:
line += " ."
else:
line += " "
lines.append(line)
return "\n".join(lines)
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename) -> Maze:
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename) -> Maze:
with open(filename, encoding="utf-8") as f:
lines = [l.rstrip("\n") for l in f]
height = len(lines)
width = max(len(l) for l in lines)
cells = []
start = exit_cell = None
for y, line in enumerate(lines):
row = []
for x in range(width):
ch = line[x] if x < len(line) else " "
is_wall = ch == "#"
is_start = ch == "S"
is_exit = ch == "E"
c = Cell(x, y, is_wall, is_start, is_exit)
if is_start:
start = c
if is_exit:
exit_cell = c
row.append(c)
cells.append(row)
if not start or not exit_cell:
raise ValueError("Maze must have S and E")
return Maze(cells, width, height, start, exit_cell)
@dataclass
class SearchStats:
strategy: str
time_ms: float
visited: int
path_length: int
path: List[Cell] = field(default_factory=list)
class PathFindingStrategy(ABC):
_visited = 0
@property
def name(self):
return self.__class__.__name__
@abstractmethod
def find_path(self, maze: Maze, start: Cell, end: Cell) -> List[Cell]:
pass
@staticmethod
def _build_path(parent, start, end):
path, cur = [], end
while cur:
path.append(cur)
cur = parent.get(cur)
path.reverse()
return path if path and path[0] == start else []
class BFSStrategy(PathFindingStrategy):
@property
def name(self):
return "BFS"
def find_path(self, maze, start, end):
queue = deque([start])
parent = {start: None}
visited = 0
while queue:
cur = queue.popleft()
visited += 1
if cur == end:
self._visited = visited
return self._build_path(parent, start, end)
for nb in maze.get_neighbors(cur):
if nb not in parent:
parent[nb] = cur
queue.append(nb)
self._visited = visited
return []
class DFSStrategy(PathFindingStrategy):
@property
def name(self):
return "DFS"
def find_path(self, maze, start, end):
stack = [start]
parent = {start: None}
visited = 0
while stack:
cur = stack.pop()
visited += 1
if cur == end:
self._visited = visited
return self._build_path(parent, start, end)
for nb in maze.get_neighbors(cur):
if nb not in parent:
parent[nb] = cur
stack.append(nb)
self._visited = visited
return []
class AStarStrategy(PathFindingStrategy):
@property
def name(self):
return "A*"
@staticmethod
def _h(a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze, start, end):
counter = 0
heap = [(0, counter, start)]
parent = {start: None}
g = {start: 0}
closed = set()
visited = 0
while heap:
_, _, cur = heapq.heappop(heap)
if cur in closed:
continue
closed.add(cur)
visited += 1
if cur == end:
self._visited = visited
return self._build_path(parent, start, end)
for nb in maze.get_neighbors(cur):
if nb in closed:
continue
ng = g[cur] + 1
if ng < g.get(nb, float("inf")):
g[nb] = ng
counter += 1
heapq.heappush(heap, (ng + self._h(nb, end), counter, nb))
parent[nb] = cur
self._visited = visited
return []
class MazeSolver:
def __init__(self, maze: Maze, strategy: PathFindingStrategy):
self.maze = maze
self.strategy = strategy
def set_strategy(self, strategy: PathFindingStrategy):
self.strategy = strategy
def solve(self) -> SearchStats:
t0 = time.perf_counter()
path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
t1 = time.perf_counter()
return SearchStats(
strategy=self.strategy.name,
time_ms=(t1 - t0) * 1000,
visited=self.strategy._visited,
path_length=len(path),
path=path
)

View File

@ -0,0 +1,78 @@
import os
import random
def _backtracker(width, height, seed=42):
rng = random.Random(seed)
cw = (width - 1) // 2
ch = (height - 1) // 2
grid = [["#"] * width for _ in range(height)]
visited = [[False] * cw for _ in range(ch)]
stack = [(0, 0)]
visited[0][0] = True
grid[1][1] = " "
while stack:
cx, cy = stack[-1]
gx, gy = cx * 2 + 1, cy * 2 + 1
dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)]
rng.shuffle(dirs)
moved = False
for dx, dy in dirs:
nx, ny = cx + dx, cy + dy
if 0 <= nx < cw and 0 <= ny < ch and not visited[ny][nx]:
visited[ny][nx] = True
grid[gy + dy][gx + dx] = " "
grid[ny * 2 + 1][nx * 2 + 1] = " "
stack.append((nx, ny))
moved = True
break
if not moved:
stack.pop()
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return grid
def _empty(width, height):
grid = [["#"] * width for _ in range(height)]
for y in range(1, height - 1):
for x in range(1, width - 1):
grid[y][x] = " "
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return grid
def _no_exit(width=11, height=11):
grid = _backtracker(width, height, seed=99)
for y in range(height):
for x in range(width):
if grid[y][x] == "E":
grid[y][x] = "#"
grid[1][width - 2] = "E"
for dy in [-1, 0, 1]:
for dx in [-1, 0, 1]:
ny, nx = 1 + dy, (width - 2) + dx
if 0 <= ny < height and 0 <= nx < width and grid[ny][nx] != "E":
grid[ny][nx] = "#"
return grid
def _save(grid, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
for row in grid:
f.write("".join(row) + "\n")
def generate_all(folder="mazes"):
mazes = {
"small.txt": _backtracker(11, 11, seed=1),
"medium.txt": _backtracker(51, 51, seed=2),
"large.txt": _backtracker(101, 101, seed=3),
"empty.txt": _empty(51, 21),
"no_exit.txt": _no_exit(11, 11),
"sample.txt": _backtracker(15, 15, seed=5),
}
for name, grid in mazes.items():
_save(grid, os.path.join(folder, name))
print(f"Mazes saved to {folder}/")

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -0,0 +1,193 @@
# Отчёт: Поиск выхода из лабиринта
## 1. Описание задачи и выбранных паттернов
### Задача
Разработать программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода тремя алгоритмами (BFS, DFS, A*), визуализации найденного пути и экспериментального сравнения алгоритмов по времени, числу посещённых клеток и длине пути.
### Структура файлов
```
02/
main.py - точка запуска
codes/
maze.py - все классы (Cell, Maze, Builder, Strategy, Solver)
maze_generator.py - генерация тестовых лабиринтов
mazes/ - текстовые файлы лабиринтов
results/
results_maze.csv - результаты экспериментов
benchmark_plot.png - графики
docs/
report1.md - отчёт
mermaid.png - диаграмма классов
```
### Применённые паттерны проектирования
**1. Builder** - класс `TextFileMazeBuilder` реализует интерфейс `MazeBuilder`.
Построение лабиринта из файла включает несколько шагов: чтение строк, обход символов, создание объектов `Cell`, поиск стартовой и конечной клетки. Без Builder вся эта логика оказалась бы в `main.py` или в конструкторе `Maze`. Builder скрывает детали создания от клиента. Если понадобится загружать лабиринт из JSON или бинарного файла - достаточно написать новый класс, реализующий тот же интерфейс `MazeBuilder`.
**2. Strategy** - классы `BFSStrategy`, `DFSStrategy`, `AStarStrategy` реализуют интерфейс `PathFindingStrategy`.
Алгоритм поиска можно менять во время работы программы через `MazeSolver.set_strategy()`, не трогая остальной код. Добавление нового алгоритма - это написание одного нового класса с методом `find_path()`. Без Strategy в `solve()` пришлось бы писать if/elif для каждого алгоритма.
**3. Observer** - интерфейс `Observer` с методом `update(event)`.
`MazeSolver` хранит список наблюдателей и уведомляет их при событиях `search_started`, `path_found`, `path_not_found`. Это позволяет добавлять отображение в консоль, запись в лог или GUI-уведомления, не меняя код солвера. Слабая связанность: солвер не знает, кто его слушает.
### Диаграмма классов
![Диаграмма классов](mermaid.png)
---
## 2. Листинги ключевых классов
### Cell и Maze
```python
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
def is_passable(self):
return not self.is_wall
class Maze:
def get_neighbors(self, cell):
result = []
for dx, dy in [(0,-1),(0,1),(-1,0),(1,0)]:
n = self.get_cell(cell.x + dx, cell.y + dy)
if n and n.is_passable():
result.append(n)
return result
```
### Паттерн Builder
```python
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename) -> Maze:
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename) -> Maze:
with open(filename, encoding="utf-8") as f:
lines = [l.rstrip("\n") for l in f]
# ... парсинг символов, создание Cell, поиск S и E
return Maze(cells, width, height, start, exit_cell)
```
### Паттерн Strategy - алгоритм A*
```python
class AStarStrategy(PathFindingStrategy):
@staticmethod
def _h(a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze, start, end):
heap = [(0, 0, start)]
parent = {start: None}
g = {start: 0}
closed = set()
while heap:
_, _, cur = heapq.heappop(heap)
if cur in closed:
continue
closed.add(cur)
if cur == end:
return self._build_path(parent, start, end)
for nb in maze.get_neighbors(cur):
ng = g[cur] + 1
if ng < g.get(nb, float("inf")):
g[nb] = ng
heapq.heappush(heap, (ng + self._h(nb, end), id(nb), nb))
parent[nb] = cur
return []
```
### MazeSolver
```python
class MazeSolver:
def __init__(self, maze, strategy):
self.maze = maze
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def solve(self) -> SearchStats:
t0 = time.perf_counter()
path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
t1 = time.perf_counter()
return SearchStats(
strategy=self.strategy.name,
time_ms=(t1 - t0) * 1000,
visited=self.strategy._visited,
path_length=len(path),
path=path,
)
```
---
## 3. Результаты экспериментов
Каждый алгоритм запускался 7 раз на каждом лабиринте, результаты усреднялись.
### Таблица результатов
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|----------|----------|-----------|----------------|------------|
| small (11x11) | BFS | 0.070 | 39 | 33 |
| small (11x11) | DFS | 0.055 | 33 | 33 |
| small (11x11) | A* | 0.112 | 35 | 33 |
| medium (51x51) | BFS | 1.391 | 793 | 497 |
| medium (51x51) | DFS | 0.949 | 515 | 497 |
| medium (51x51) | A* | 2.271 | 707 | 497 |
| large (101x101) | BFS | 6.231 | 3533 | 1613 |
| large (101x101) | DFS | 3.341 | 1957 | 1613 |
| large (101x101) | A* | 11.27 | 3379 | 1613 |
| empty (51x21) | BFS | 1.992 | 931 | 67 |
| empty (51x21) | DFS | 1.021 | 451 | 451 |
| empty (51x21) | A* | 3.527 | 931 | 67 |
| no_exit (11x11) | BFS | 0.079 | 40 | - |
| no_exit (11x11) | DFS | 0.077 | 40 | - |
| no_exit (11x11) | A* | 0.140 | 40 | - |
### Графики
![Графики](../results/benchmark_plot.png)
---
## 4. Анализ эффективности алгоритмов и применимости паттернов
### Алгоритмы
**BFS** гарантирует кратчайший путь по числу шагов. Расширяет узлы слой за слоем во всех направлениях, поэтому посещает наибольшее число клеток. На практике это надёжный выбор когда нужен точно кратчайший маршрут.
**DFS** посещает меньше клеток и выполняется быстрее - на large лабиринте в 1.8 раза быстрее BFS. Однако путь может быть далеко не кратчайшим. На пустом лабиринте DFS нашёл путь длиной 451 шаг, тогда как BFS и A* - 67. Это связано с тем, что DFS уходит в первое попавшееся направление и возвращается только в тупике.
**A*** использует манхэттенскую эвристику h = |x1-x2| + |y1-y2| и должен в теории посещать меньше клеток чем BFS. На лабиринтах, сгенерированных алгоритмом recursive backtracker, выигрыш небольшой (примерно 5%). Причина: backtracker строит дерево - между любыми двумя клетками ровно один путь, тупиков нет, эвристика не помогает их обходить. На лабиринтах с циклами A* посещает заметно меньше клеток. Накладные расходы на работу с heap и closed-set делают A* медленнее по времени, чем DFS.
На пустом лабиринте (без стен) A* ведёт себя как BFS. Математически: f(x,y) = g + h = (x-1+y-1) + (W-x+H-y) = const для всех клеток. Все узлы неразличимы по приоритету.
На лабиринте без выхода все три алгоритма посещают одинаковое число клеток и корректно возвращают пустой путь.
### Паттерны
**Builder** оказался полезным при добавлении нового типа лабиринта (взвешенного, с символами s и m). Изменения были внесены только в `TextFileMazeBuilder`, клиентский код не менялся.
**Strategy** позволил в одном цикле запустить все три алгоритма через `solver.set_strategy(strategy)`. Без паттерна пришлось бы либо дублировать код запуска для каждого алгоритма, либо писать условные ветки.
**Observer** полезен при расширении: чтобы добавить вывод в лог или консоль, достаточно написать новый Observer и подписать его на solver, не меняя `MazeSolver`.
---
## 5. Выводы
ООП и паттерны позволили сделать код гибким в нескольких направлениях.
Добавление нового алгоритма поиска сводится к написанию одного класса, реализующего `find_path()`. Без Strategy пришлось бы добавлять ветку в `solve()` и во все места, где запускается поиск.
Добавление нового формата лабиринта - только новый класс Builder. Без паттерна логика парсинга была бы перемешана с логикой работы программы.
Добавление нового способа отображения (GUI, запись в файл) - только новый Observer. Без него MazeSolver пришлось бы напрямую вызывать функции отображения, что создало бы зависимость от конкретной реализации.
Без применения паттернов код решал бы задачу, но любое изменение требовало бы правки в нескольких местах сразу. С паттернами каждый класс отвечает за одну задачу и не знает о деталях реализации соседних классов.

165
VasilevIA/lab2/main.py Normal file
View File

@ -0,0 +1,165 @@
import csv
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "codes"))
from maze import TextFileMazeBuilder, MazeSolver, BFSStrategy, DFSStrategy, AStarStrategy
from maze_generator import generate_all
try:
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use("Agg")
HAS_PLT = True
except ImportError:
HAS_PLT = False
BASE_DIR = os.path.dirname(__file__)
MAZES_DIR = os.path.join(BASE_DIR, "mazes")
RESULTS_DIR = os.path.join(BASE_DIR, "results")
RUNS = 7
MAZE_FILES = [
("small", "small.txt"),
("medium", "medium.txt"),
("large", "large.txt"),
("empty", "empty.txt"),
("no_exit", "no_exit.txt"),
]
def run():
os.makedirs(RESULTS_DIR, exist_ok=True)
if not os.path.exists(MAZES_DIR) or not os.listdir(MAZES_DIR):
generate_all(MAZES_DIR)
strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy()]
builder = TextFileMazeBuilder()
all_results = []
for label, filename in MAZE_FILES:
path = os.path.join(MAZES_DIR, filename)
if not os.path.exists(path):
continue
maze = builder.build_from_file(path)
print(f"\nMaze: {label} ({maze.width}x{maze.height})")
solver = MazeSolver(maze, strategies[0])
for strategy in strategies:
solver.set_strategy(strategy)
times, visited_list, lengths = [], [], []
for _ in range(RUNS):
stats = solver.solve()
times.append(stats.time_ms)
visited_list.append(stats.visited)
lengths.append(stats.path_length)
avg_time = sum(times) / RUNS
avg_visited = sum(visited_list) / RUNS
avg_len = sum(lengths) / RUNS
found = f"length={avg_len:.0f}" if avg_len > 0 else "not found"
print(f" {strategy.name:<6} time={avg_time:.4f} ms visited={avg_visited:.0f} {found}")
all_results.append({
"maze": label,
"strategy": strategy.name,
"time_ms": round(avg_time, 4),
"visited_cells": round(avg_visited, 1),
"path_length": round(avg_len, 1),
})
save_csv(all_results)
save_plots(all_results)
show_sample()
print("\nDone. See results/ and docs/")
def save_csv(results):
path = os.path.join(RESULTS_DIR, "results_maze.csv")
with open(path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(
f, fieldnames=["maze", "strategy", "time_ms", "visited_cells", "path_length"]
)
writer.writeheader()
writer.writerows(results)
print(f"\nCSV saved: {path}")
def save_plots(results):
if not HAS_PLT:
return
mazes = list(dict.fromkeys(r["maze"] for r in results))
strategies = list(dict.fromkeys(r["strategy"] for r in results))
colors = ["#2196F3", "#FF5722", "#4CAF50"]
def val(maze, strat, key):
for r in results:
if r["maze"] == maze and r["strategy"] == strat:
return float(r[key])
return 0.0
metrics = [
("time_ms", "Time (ms)"),
("visited_cells", "Visited cells"),
("path_length", "Path length"),
]
fig, axes = plt.subplots(
len(metrics), len(mazes),
figsize=(3.5 * len(mazes), 4 * len(metrics))
)
def fmt(v):
if v == 0:
return "0"
if v >= 100:
return f"{v:.0f}"
if v >= 1:
return f"{v:.2f}"
return f"{v:.3f}"
for row_i, (key, ylabel) in enumerate(metrics):
for col_i, maze in enumerate(mazes):
ax = axes[row_i][col_i]
vals = [val(maze, s, key) for s in strategies]
bars = ax.bar(strategies, vals, color=colors[:len(strategies)])
if row_i == 0:
ax.set_title(maze, fontsize=9)
if col_i == 0:
ax.set_ylabel(ylabel)
for bar, v in zip(bars, vals):
ax.text(
bar.get_x() + bar.get_width() / 2,
bar.get_height() * 1.02,
fmt(v), ha="center", va="bottom", fontsize=7
)
ax.tick_params(axis="x", labelsize=8)
plt.tight_layout()
out = os.path.join(RESULTS_DIR, "benchmark_plot.png")
plt.savefig(out, dpi=120)
plt.close()
print(f"Chart saved: {out}")
def show_sample():
path = os.path.join(MAZES_DIR, "sample.txt")
if not os.path.exists(path):
return
builder = TextFileMazeBuilder()
maze = builder.build_from_file(path)
solver = MazeSolver(maze, BFSStrategy())
stats = solver.solve()
print("\nSample maze with BFS path:")
print(maze.render(path=stats.path))
if __name__ == "__main__":
run()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -0,0 +1,16 @@
maze,strategy,time_ms,visited_cells,path_length
small,BFS,0.0676,39.0,33.0
small,DFS,0.061,33.0,33.0
small,A*,0.1093,35.0,33.0
medium,BFS,1.4027,793.0,497.0
medium,DFS,0.8985,515.0,497.0
medium,A*,2.3001,707.0,497.0
large,BFS,6.1605,3533.0,1613.0
large,DFS,3.3919,1957.0,1613.0
large,A*,11.2172,3379.0,1613.0
empty,BFS,1.7583,931.0,67.0
empty,DFS,1.0076,451.0,451.0
empty,A*,3.4836,931.0,67.0
no_exit,BFS,0.067,40.0,0.0
no_exit,DFS,0.0599,40.0,0.0
no_exit,A*,0.1099,40.0,0.0
1 maze strategy time_ms visited_cells path_length
2 small BFS 0.0676 39.0 33.0
3 small DFS 0.061 33.0 33.0
4 small A* 0.1093 35.0 33.0
5 medium BFS 1.4027 793.0 497.0
6 medium DFS 0.8985 515.0 497.0
7 medium A* 2.3001 707.0 497.0
8 large BFS 6.1605 3533.0 1613.0
9 large DFS 3.3919 1957.0 1613.0
10 large A* 11.2172 3379.0 1613.0
11 empty BFS 1.7583 931.0 67.0
12 empty DFS 1.0076 451.0 451.0
13 empty A* 3.4836 931.0 67.0
14 no_exit BFS 0.067 40.0 0.0
15 no_exit DFS 0.0599 40.0 0.0
16 no_exit A* 0.1099 40.0 0.0