[1][2] lab1 structures and lab2 maze
This commit is contained in:
parent
8124ef4551
commit
b822b9c524
1
rybakovaa/428b.md
Normal file
1
rybakovaa/428b.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b
|
||||||
327
rybakovaa/lab1/docs/data/lab1.py
Normal file
327
rybakovaa/lab1/docs/data/lab1.py
Normal file
|
|
@ -0,0 +1,327 @@
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.setrecursionlimit(20000)
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
DATA_PATH = BASE
|
||||||
|
|
||||||
|
N = 10000
|
||||||
|
REPEAT = 5
|
||||||
|
|
||||||
|
|
||||||
|
def ll_insert(head, name, phone):
|
||||||
|
new_node = {"name": name, "phone": phone, "next": head}
|
||||||
|
return new_node
|
||||||
|
|
||||||
|
|
||||||
|
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"]
|
||||||
|
return head
|
||||||
|
curr = curr["next"]
|
||||||
|
return head
|
||||||
|
|
||||||
|
|
||||||
|
def ll_list_all(head):
|
||||||
|
result = []
|
||||||
|
curr = head
|
||||||
|
while curr:
|
||||||
|
result.append((curr["name"], curr["phone"]))
|
||||||
|
curr = curr["next"]
|
||||||
|
result.sort()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
BUCKET_SIZE = 1000
|
||||||
|
|
||||||
|
|
||||||
|
def ht_insert(buckets, name, phone):
|
||||||
|
idx = hash(name) % len(buckets)
|
||||||
|
buckets[idx] = ll_insert(buckets[idx], name, phone)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_find(buckets, name):
|
||||||
|
idx = hash(name) % len(buckets)
|
||||||
|
return ll_find(buckets[idx], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_delete(buckets, name):
|
||||||
|
idx = hash(name) % len(buckets)
|
||||||
|
buckets[idx] = ll_delete(buckets[idx], name)
|
||||||
|
|
||||||
|
|
||||||
|
def ht_list_all(buckets):
|
||||||
|
result = []
|
||||||
|
for bucket in buckets:
|
||||||
|
curr = bucket
|
||||||
|
while curr:
|
||||||
|
result.append((curr["name"], curr["phone"]))
|
||||||
|
curr = curr["next"]
|
||||||
|
result.sort()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
if root is None:
|
||||||
|
return None
|
||||||
|
if name == root["name"]:
|
||||||
|
return root["phone"]
|
||||||
|
if name < root["name"]:
|
||||||
|
return bst_find(root["left"], name)
|
||||||
|
return bst_find(root["right"], name)
|
||||||
|
|
||||||
|
|
||||||
|
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"]
|
||||||
|
temp = root["right"]
|
||||||
|
while temp["left"]:
|
||||||
|
temp = temp["left"]
|
||||||
|
root["name"] = temp["name"]
|
||||||
|
root["phone"] = temp["phone"]
|
||||||
|
root["right"] = bst_delete(root["right"], temp["name"])
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def bst_list_all(root):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
def walk(node):
|
||||||
|
if node is None:
|
||||||
|
return
|
||||||
|
walk(node["left"])
|
||||||
|
result.append((node["name"], node["phone"]))
|
||||||
|
walk(node["right"])
|
||||||
|
|
||||||
|
walk(root)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def make_records(n):
|
||||||
|
records = []
|
||||||
|
for i in range(n):
|
||||||
|
records.append((f"User_{i:05d}", f"8-900-{i % 10000:04d}"))
|
||||||
|
return records
|
||||||
|
|
||||||
|
|
||||||
|
records_all = make_records(N)
|
||||||
|
records_shuffled = records_all[:]
|
||||||
|
random.shuffle(records_shuffled)
|
||||||
|
records_sorted = sorted(records_all)
|
||||||
|
|
||||||
|
all_names = [name for name, phone in records_all]
|
||||||
|
find_existing = random.sample(all_names, 100)
|
||||||
|
find_missing = [f"None_{i}" for i in range(10)]
|
||||||
|
find_names = find_existing + find_missing
|
||||||
|
random.shuffle(find_names)
|
||||||
|
delete_names = random.sample(all_names, 50)
|
||||||
|
|
||||||
|
all_results = []
|
||||||
|
summary = []
|
||||||
|
|
||||||
|
|
||||||
|
def build_structure(struct_type, records):
|
||||||
|
if struct_type == "LinkedList":
|
||||||
|
head = None
|
||||||
|
for name, phone in records:
|
||||||
|
head = ll_insert(head, name, phone)
|
||||||
|
return head
|
||||||
|
if struct_type == "HashTable":
|
||||||
|
buckets = [None] * BUCKET_SIZE
|
||||||
|
for name, phone in records:
|
||||||
|
ht_insert(buckets, name, phone)
|
||||||
|
return buckets
|
||||||
|
root = None
|
||||||
|
for name, phone in records:
|
||||||
|
root = bst_insert(root, name, phone)
|
||||||
|
return root
|
||||||
|
|
||||||
|
|
||||||
|
def do_find(struct_type, container, names):
|
||||||
|
for name in names:
|
||||||
|
if struct_type == "LinkedList":
|
||||||
|
ll_find(container, name)
|
||||||
|
elif struct_type == "HashTable":
|
||||||
|
ht_find(container, name)
|
||||||
|
else:
|
||||||
|
bst_find(container, name)
|
||||||
|
|
||||||
|
|
||||||
|
def do_delete(struct_type, container, names):
|
||||||
|
if struct_type == "LinkedList":
|
||||||
|
for name in names:
|
||||||
|
container = ll_delete(container, name)
|
||||||
|
return container
|
||||||
|
if struct_type == "HashTable":
|
||||||
|
for name in names:
|
||||||
|
ht_delete(container, name)
|
||||||
|
return container
|
||||||
|
for name in names:
|
||||||
|
container = bst_delete(container, name)
|
||||||
|
return container
|
||||||
|
|
||||||
|
|
||||||
|
def run_one_test(struct_type, mode_name, records):
|
||||||
|
ins_times = []
|
||||||
|
find_times = []
|
||||||
|
del_times = []
|
||||||
|
|
||||||
|
for run in range(REPEAT):
|
||||||
|
start = time.perf_counter()
|
||||||
|
container = build_structure(struct_type, records)
|
||||||
|
ins_times.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
do_find(struct_type, container, find_names)
|
||||||
|
find_times.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
do_delete(struct_type, container, delete_names)
|
||||||
|
del_times.append(time.perf_counter() - start)
|
||||||
|
|
||||||
|
all_results.append([
|
||||||
|
struct_type, mode_name, f"Run {run + 1}",
|
||||||
|
ins_times[-1], find_times[-1], del_times[-1],
|
||||||
|
])
|
||||||
|
|
||||||
|
avg_ins = sum(ins_times) / REPEAT
|
||||||
|
avg_find = sum(find_times) / REPEAT
|
||||||
|
avg_del = sum(del_times) / REPEAT
|
||||||
|
|
||||||
|
all_results.append([
|
||||||
|
struct_type, mode_name, "AVERAGE", avg_ins, avg_find, avg_del,
|
||||||
|
])
|
||||||
|
summary.append({
|
||||||
|
"name": struct_type,
|
||||||
|
"mode": mode_name,
|
||||||
|
"ins": avg_ins,
|
||||||
|
"find": avg_find,
|
||||||
|
"del": avg_del,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
print("Запуск экспериментов...")
|
||||||
|
for mode_name, data in [("случайный", records_shuffled), ("сортированный", records_sorted)]:
|
||||||
|
for struct_type in ["LinkedList", "HashTable", "BST"]:
|
||||||
|
print(f" {struct_type} ({mode_name})")
|
||||||
|
run_one_test(struct_type, mode_name, data)
|
||||||
|
|
||||||
|
csv_path = os.path.join(DATA_PATH, "results.csv")
|
||||||
|
with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
|
||||||
|
writer = csv.writer(f, delimiter=";")
|
||||||
|
writer.writerow(["Структура", "Режим", "Итерация", "Вставка", "Поиск", "Удаление"])
|
||||||
|
writer.writerows(all_results)
|
||||||
|
|
||||||
|
print("CSV сохранён:", csv_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
plt.rcParams["font.sans-serif"] = ["Segoe UI", "Arial", "Tahoma", "DejaVu Sans"]
|
||||||
|
plt.rcParams["axes.unicode_minus"] = False
|
||||||
|
|
||||||
|
labels = ["insert", "find", "delete"]
|
||||||
|
structs = ["LinkedList", "HashTable", "BST"]
|
||||||
|
colors = ["#5dade2", "#e67e22", "#58d68d"]
|
||||||
|
|
||||||
|
fig1, axs = plt.subplots(1, 3, figsize=(15, 5))
|
||||||
|
fig1.suptitle("Влияние порядка данных")
|
||||||
|
|
||||||
|
for i, s_name in enumerate(structs):
|
||||||
|
rand_d = next(r for r in summary if r["name"] == s_name and r["mode"] == "случайный")
|
||||||
|
sort_d = next(r for r in summary if r["name"] == s_name and r["mode"] == "сортированный")
|
||||||
|
x = [0, 1, 2]
|
||||||
|
w = 0.35
|
||||||
|
axs[i].bar([p - w / 2 for p in x], [rand_d["ins"], rand_d["find"], rand_d["del"]], w, label="случайный")
|
||||||
|
axs[i].bar([p + w / 2 for p in x], [sort_d["ins"], sort_d["find"], sort_d["del"]], w, label="сортированный")
|
||||||
|
axs[i].set_title(s_name)
|
||||||
|
axs[i].set_xticks(x)
|
||||||
|
axs[i].set_xticklabels(labels)
|
||||||
|
axs[i].legend()
|
||||||
|
axs[i].grid(axis="y", alpha=0.3)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(os.path.join(DATA_PATH, "order_impact.png"))
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
fig2, axs2 = plt.subplots(1, 3, figsize=(15, 5))
|
||||||
|
fig2.suptitle(f"Сравнение структур (N={N})")
|
||||||
|
|
||||||
|
for i, key in enumerate(["ins", "find", "del"]):
|
||||||
|
vals = []
|
||||||
|
names = []
|
||||||
|
for r in summary:
|
||||||
|
names.append(f"{r['name']}\n({r['mode'][:4]})")
|
||||||
|
vals.append(r[key])
|
||||||
|
axs2[i].bar(names, vals, color=colors * 2)
|
||||||
|
axs2[i].set_title(labels[i])
|
||||||
|
axs2[i].tick_params(axis="x", rotation=20)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(os.path.join(DATA_PATH, "struct_comparison.png"))
|
||||||
|
plt.close()
|
||||||
|
print("Графики сохранены")
|
||||||
|
except ImportError:
|
||||||
|
print("matplotlib не установлен")
|
||||||
|
|
||||||
|
report_path = os.path.join(os.path.dirname(BASE), "report.md")
|
||||||
|
with open(report_path, "w", encoding="utf-8-sig") as f:
|
||||||
|
f.write("# Отчёт: сравнение структур данных\n\n")
|
||||||
|
f.write(f"N = {N}, повторов = {REPEAT}\n\n")
|
||||||
|
f.write("| Структура | Режим | Вставка (с) | Поиск (с) | Удаление (с) |\n")
|
||||||
|
f.write("| --- | --- | --- | --- | --- |\n")
|
||||||
|
for r in summary:
|
||||||
|
f.write(
|
||||||
|
f"| {r['name']} | {r['mode']} | {r['ins']:.6f} | {r['find']:.6f} | {r['del']:.6f} |\n"
|
||||||
|
)
|
||||||
|
f.write("\n## Графики\n\n")
|
||||||
|
f.write("\n\n")
|
||||||
|
f.write("\n\n")
|
||||||
|
f.write("## Выводы\n\n")
|
||||||
|
f.write("- BST на отсортированных данных сильно тормозит (вырождение дерева).\n")
|
||||||
|
f.write("- Хеш-таблица быстра на поиске и слабо зависит от порядка вставки.\n")
|
||||||
|
f.write("- Связный список медленный при поиске.\n")
|
||||||
|
f.write("- Для частого поиска предпочтительна хеш-таблица.\n")
|
||||||
|
|
||||||
|
print("Отчёт:", report_path)
|
||||||
|
print("Готово.")
|
||||||
BIN
rybakovaa/lab1/docs/data/order_impact.png
Normal file
BIN
rybakovaa/lab1/docs/data/order_impact.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 27 KiB |
37
rybakovaa/lab1/docs/data/results.csv
Normal file
37
rybakovaa/lab1/docs/data/results.csv
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
Структура;Режим;Итерация;Вставка;Поиск;Удаление
|
||||||
|
LinkedList;случайный;Run 1;0.002766899997368455;0.027239699964411557;0.015202199923805892
|
||||||
|
LinkedList;случайный;Run 2;0.0023452999303117394;0.02690729999449104;0.014689600095152855
|
||||||
|
LinkedList;случайный;Run 3;0.0026440999936312437;0.028060800046660006;0.01486769993789494
|
||||||
|
LinkedList;случайный;Run 4;0.002523000002838671;0.02711769996676594;0.014554499997757375
|
||||||
|
LinkedList;случайный;Run 5;0.0022324000019580126;0.02935329999309033;0.015334900002926588
|
||||||
|
LinkedList;случайный;AVERAGE;0.0025023399852216245;0.027735759993083774;0.01492977999150753
|
||||||
|
HashTable;случайный;Run 1;0.0037400999572128057;7.149996235966682e-05;3.490003291517496e-05
|
||||||
|
HashTable;случайный;Run 2;0.004325399990193546;9.180000051856041e-05;4.499999340623617e-05
|
||||||
|
HashTable;случайный;Run 3;0.006647299975156784;9.760004468262196e-05;4.809990059584379e-05
|
||||||
|
HashTable;случайный;Run 4;0.004817900015041232;8.430005982518196e-05;4.279997665435076e-05
|
||||||
|
HashTable;случайный;Run 5;0.0045270000118762255;7.889990229159594e-05;3.660004585981369e-05
|
||||||
|
HashTable;случайный;AVERAGE;0.004811539989896118;8.481999393552541e-05;4.147998988628388e-05
|
||||||
|
BST;случайный;Run 1;0.020208499976433814;0.00017140002455562353;0.000107599887996912
|
||||||
|
BST;случайный;Run 2;0.02269990008790046;0.00022380007430911064;0.0001463999506086111
|
||||||
|
BST;случайный;Run 3;0.022515299962833524;0.00019350007642060518;0.00011879997327923775
|
||||||
|
BST;случайный;Run 4;0.02134259999729693;0.00019699998665601015;0.0001330999657511711
|
||||||
|
BST;случайный;Run 5;0.022310999920591712;0.00020180002320557833;0.00011969998013228178
|
||||||
|
BST;случайный;AVERAGE;0.02181545998901129;0.00019750003702938556;0.00012511995155364274
|
||||||
|
LinkedList;сортированный;Run 1;0.0014724000357091427;0.024460599990561604;0.016624199924990535
|
||||||
|
LinkedList;сортированный;Run 2;0.0026603000005707145;0.02619360003154725;0.015555899939499795
|
||||||
|
LinkedList;сортированный;Run 3;0.003988999989815056;0.026726300013251603;0.016439199913293123
|
||||||
|
LinkedList;сортированный;Run 4;0.003310499945655465;0.024290600093081594;0.016939799999818206
|
||||||
|
LinkedList;сортированный;Run 5;0.003344499971717596;0.02642290003132075;0.016576700028963387
|
||||||
|
LinkedList;сортированный;AVERAGE;0.002955339988693595;0.02561880003195256;0.01642715996131301
|
||||||
|
HashTable;сортированный;Run 1;0.00349499995354563;9.34000127017498e-05;5.8999983593821526e-05
|
||||||
|
HashTable;сортированный;Run 2;0.004315900034271181;0.00011070002801716328;5.6999968364834785e-05
|
||||||
|
HashTable;сортированный;Run 3;0.004093199968338013;8.140003774315119e-05;4.549999721348286e-05
|
||||||
|
HashTable;сортированный;Run 4;0.004008699906989932;8.000002708286047e-05;4.539999645203352e-05
|
||||||
|
HashTable;сортированный;Run 5;0.004412899957969785;7.609999738633633e-05;4.290009383112192e-05
|
||||||
|
HashTable;сортированный;AVERAGE;0.004065139964222908;8.832002058625221e-05;4.996000789105892e-05
|
||||||
|
BST;сортированный;Run 1;8.548112499993294;0.06775930000003427;0.03638990002218634
|
||||||
|
BST;сортированный;Run 2;8.337813499965705;0.06507849995978177;0.03630929999053478
|
||||||
|
BST;сортированный;Run 3;8.455186700099148;0.06767350004520267;0.036670299945399165
|
||||||
|
BST;сортированный;Run 4;8.47301429999061;0.06812409998383373;0.037254099966958165
|
||||||
|
BST;сортированный;Run 5;8.588394599966705;0.06450700003188103;0.03623760002665222
|
||||||
|
BST;сортированный;AVERAGE;8.480504320003092;0.0666284800041467;0.03657223999034613
|
||||||
|
BIN
rybakovaa/lab1/docs/data/struct_comparison.png
Normal file
BIN
rybakovaa/lab1/docs/data/struct_comparison.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
25
rybakovaa/lab1/docs/report.md
Normal file
25
rybakovaa/lab1/docs/report.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Отчёт: сравнение структур данных
|
||||||
|
|
||||||
|
N = 10000, повторов = 5
|
||||||
|
|
||||||
|
| Структура | Режим | Вставка (с) | Поиск (с) | Удаление (с) |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| LinkedList | случайный | 0.002502 | 0.027736 | 0.014930 |
|
||||||
|
| HashTable | случайный | 0.004812 | 0.000085 | 0.000041 |
|
||||||
|
| BST | случайный | 0.021815 | 0.000198 | 0.000125 |
|
||||||
|
| LinkedList | сортированный | 0.002955 | 0.025619 | 0.016427 |
|
||||||
|
| HashTable | сортированный | 0.004065 | 0.000088 | 0.000050 |
|
||||||
|
| BST | сортированный | 8.480504 | 0.066628 | 0.036572 |
|
||||||
|
|
||||||
|
## Графики
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Выводы
|
||||||
|
|
||||||
|
- BST на отсортированных данных сильно тормозит (вырождение дерева).
|
||||||
|
- Хеш-таблица быстра на поиске и слабо зависит от порядка вставки.
|
||||||
|
- Связный список медленный при поиске.
|
||||||
|
- Для частого поиска предпочтительна хеш-таблица.
|
||||||
428
rybakovaa/lab2/docs/data/laba2.py
Normal file
428
rybakovaa/lab2/docs/data/laba2.py
Normal file
|
|
@ -0,0 +1,428 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from collections import deque
|
||||||
|
import heapq
|
||||||
|
import time
|
||||||
|
import csv
|
||||||
|
import random
|
||||||
|
import os
|
||||||
|
|
||||||
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class Cell:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.isWall = False
|
||||||
|
self.isStart = False
|
||||||
|
self.isExit = False
|
||||||
|
|
||||||
|
def isPassable(self):
|
||||||
|
return not self.isWall
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if other is None:
|
||||||
|
return False
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.x, self.y))
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return (self.x, self.y) < (other.x, other.y)
|
||||||
|
|
||||||
|
|
||||||
|
class Maze:
|
||||||
|
def __init__(self, width, height):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.grid = [[Cell(x, y) for y in range(height)] for x in range(width)]
|
||||||
|
self.start = None
|
||||||
|
self.exit = None
|
||||||
|
|
||||||
|
def getCell(self, x, y):
|
||||||
|
if 0 <= x < self.width and 0 <= y < self.height:
|
||||||
|
return self.grid[x][y]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getNeighbors(self, cell):
|
||||||
|
neighbors = []
|
||||||
|
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
||||||
|
n = self.getCell(cell.x + dx, cell.y + dy)
|
||||||
|
if n and n.isPassable():
|
||||||
|
neighbors.append(n)
|
||||||
|
return neighbors
|
||||||
|
|
||||||
|
|
||||||
|
class MazeBuilder(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def buildFromFile(self, filename):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TextFileMazeBuilder(MazeBuilder):
|
||||||
|
def buildFromFile(self, filename):
|
||||||
|
with open(filename, "r", encoding="utf-8") as f:
|
||||||
|
lines = [line.rstrip("\n\r") for line in f.readlines()]
|
||||||
|
|
||||||
|
height = len(lines)
|
||||||
|
width = len(lines[0]) if height > 0 else 0
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if len(line) != width:
|
||||||
|
raise ValueError("все строки должны быть одной длины")
|
||||||
|
|
||||||
|
maze = Maze(width, height)
|
||||||
|
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
ch = lines[y][x]
|
||||||
|
cell = maze.getCell(x, y)
|
||||||
|
if ch == "#":
|
||||||
|
cell.isWall = True
|
||||||
|
elif ch == " ":
|
||||||
|
cell.isWall = False
|
||||||
|
elif ch == "S":
|
||||||
|
cell.isWall = False
|
||||||
|
cell.isStart = True
|
||||||
|
maze.start = cell
|
||||||
|
elif ch == "E":
|
||||||
|
cell.isWall = False
|
||||||
|
cell.isExit = True
|
||||||
|
maze.exit = cell
|
||||||
|
else:
|
||||||
|
raise ValueError(f"неизвестный символ: {ch}")
|
||||||
|
|
||||||
|
if maze.start is None:
|
||||||
|
raise ValueError("нет старта (S)")
|
||||||
|
if maze.exit is None:
|
||||||
|
raise ValueError("нет выхода (E)")
|
||||||
|
|
||||||
|
return maze
|
||||||
|
|
||||||
|
|
||||||
|
class PathFindingStrategy(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def findPath(self, maze, start, exit_cell):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _reconstruct(self, parent, exit_cell):
|
||||||
|
path = []
|
||||||
|
curr = exit_cell
|
||||||
|
while curr is not None:
|
||||||
|
path.append(curr)
|
||||||
|
curr = parent.get(curr)
|
||||||
|
path.reverse()
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
class BFSStrategy(PathFindingStrategy):
|
||||||
|
def findPath(self, maze, start, exit_cell):
|
||||||
|
if exit_cell is None:
|
||||||
|
return []
|
||||||
|
queue = deque([start])
|
||||||
|
visited = {start}
|
||||||
|
parent = {start: None}
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
curr = queue.popleft()
|
||||||
|
if curr == exit_cell:
|
||||||
|
return self._reconstruct(parent, exit_cell)
|
||||||
|
for n in maze.getNeighbors(curr):
|
||||||
|
if n not in visited:
|
||||||
|
visited.add(n)
|
||||||
|
parent[n] = curr
|
||||||
|
queue.append(n)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class DFSStrategy(PathFindingStrategy):
|
||||||
|
def findPath(self, maze, start, exit_cell):
|
||||||
|
if exit_cell is None:
|
||||||
|
return []
|
||||||
|
stack = [start]
|
||||||
|
visited = {start}
|
||||||
|
parent = {start: None}
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
curr = stack.pop()
|
||||||
|
if curr == exit_cell:
|
||||||
|
return self._reconstruct(parent, exit_cell)
|
||||||
|
for n in maze.getNeighbors(curr):
|
||||||
|
if n not in visited:
|
||||||
|
visited.add(n)
|
||||||
|
parent[n] = curr
|
||||||
|
stack.append(n)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class AStarStrategy(PathFindingStrategy):
|
||||||
|
def _heuristic(self, cell, exit_cell):
|
||||||
|
return abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y)
|
||||||
|
|
||||||
|
def findPath(self, maze, start, exit_cell):
|
||||||
|
if exit_cell is None:
|
||||||
|
return []
|
||||||
|
open_set = []
|
||||||
|
heapq.heappush(open_set, (0, start))
|
||||||
|
parent = {start: None}
|
||||||
|
g_score = {start: 0}
|
||||||
|
|
||||||
|
while open_set:
|
||||||
|
curr = heapq.heappop(open_set)[1]
|
||||||
|
if curr == exit_cell:
|
||||||
|
return self._reconstruct(parent, exit_cell)
|
||||||
|
|
||||||
|
for n in maze.getNeighbors(curr):
|
||||||
|
new_g = g_score[curr] + 1
|
||||||
|
if n not in g_score or new_g < g_score[n]:
|
||||||
|
g_score[n] = new_g
|
||||||
|
parent[n] = curr
|
||||||
|
f = new_g + self._heuristic(n, exit_cell)
|
||||||
|
heapq.heappush(open_set, (f, n))
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class SearchStats:
|
||||||
|
def __init__(self, time_ms, visited, path_len):
|
||||||
|
self.time_ms = time_ms
|
||||||
|
self.visited_cells = visited
|
||||||
|
self.path_length = path_len
|
||||||
|
|
||||||
|
|
||||||
|
class MazeSolver:
|
||||||
|
def __init__(self, maze):
|
||||||
|
self.maze = maze
|
||||||
|
self.strategy = None
|
||||||
|
self.observers = []
|
||||||
|
|
||||||
|
def setStrategy(self, strategy):
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
def attach(self, observer):
|
||||||
|
self.observers.append(observer)
|
||||||
|
|
||||||
|
def notify(self, event):
|
||||||
|
for obs in self.observers:
|
||||||
|
obs.update(event)
|
||||||
|
|
||||||
|
def solve(self):
|
||||||
|
if self.strategy is None:
|
||||||
|
raise ValueError("стратегия не выбрана")
|
||||||
|
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
path = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
|
||||||
|
elapsed_ms = (time.perf_counter() - start_time) * 1000
|
||||||
|
|
||||||
|
stats = SearchStats(elapsed_ms, len(path), len(path))
|
||||||
|
self.notify({"type": "path_found", "maze": self.maze, "path": path, "stats": stats})
|
||||||
|
return path, stats
|
||||||
|
|
||||||
|
|
||||||
|
class Observer(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def update(self, event):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConsoleView(Observer):
|
||||||
|
def update(self, event):
|
||||||
|
if event["type"] == "path_found":
|
||||||
|
stats = event["stats"]
|
||||||
|
print(f"длина пути {stats.path_length}, время {stats.time_ms:.2f} мс")
|
||||||
|
|
||||||
|
|
||||||
|
def save_maze(maze, filename):
|
||||||
|
path = os.path.join(BASE, filename)
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
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.isWall:
|
||||||
|
line += "#"
|
||||||
|
else:
|
||||||
|
line += " "
|
||||||
|
f.write(line + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_with_walls(w, h, prob=0.3):
|
||||||
|
maze = Maze(w, h)
|
||||||
|
for x in range(w):
|
||||||
|
for y in range(h):
|
||||||
|
if random.random() < prob:
|
||||||
|
maze.getCell(x, y).isWall = True
|
||||||
|
maze.getCell(0, 0).isWall = False
|
||||||
|
maze.getCell(w - 1, h - 1).isWall = False
|
||||||
|
for x in range(w):
|
||||||
|
maze.getCell(x, 0).isWall = False
|
||||||
|
for y in range(h):
|
||||||
|
maze.getCell(w - 1, y).isWall = False
|
||||||
|
maze.getCell(0, 0).isStart = True
|
||||||
|
maze.start = maze.getCell(0, 0)
|
||||||
|
maze.getCell(w - 1, h - 1).isExit = True
|
||||||
|
maze.exit = maze.getCell(w - 1, h - 1)
|
||||||
|
return maze
|
||||||
|
|
||||||
|
|
||||||
|
def generate_empty(w, h):
|
||||||
|
maze = Maze(w, h)
|
||||||
|
for x in range(w):
|
||||||
|
for y in range(h):
|
||||||
|
maze.getCell(x, y).isWall = False
|
||||||
|
maze.getCell(0, 0).isStart = True
|
||||||
|
maze.start = maze.getCell(0, 0)
|
||||||
|
maze.getCell(w - 1, h - 1).isExit = True
|
||||||
|
maze.exit = maze.getCell(w - 1, h - 1)
|
||||||
|
return maze
|
||||||
|
|
||||||
|
|
||||||
|
def generate_no_exit(w, h):
|
||||||
|
maze = generate_with_walls(w, h, 0.3)
|
||||||
|
exit_cell = maze.getCell(w - 1, h - 1)
|
||||||
|
exit_cell.isWall = True
|
||||||
|
exit_cell.isExit = False
|
||||||
|
maze.exit = None
|
||||||
|
return maze
|
||||||
|
|
||||||
|
|
||||||
|
def run_experiment(maze, strategy_class, maze_name, repeats=5):
|
||||||
|
times = []
|
||||||
|
path_lens = []
|
||||||
|
|
||||||
|
for _ in range(repeats):
|
||||||
|
solver = MazeSolver(maze)
|
||||||
|
solver.setStrategy(strategy_class())
|
||||||
|
path, stats = solver.solve()
|
||||||
|
times.append(stats.time_ms)
|
||||||
|
path_lens.append(len(path))
|
||||||
|
|
||||||
|
raw = strategy_class.__name__
|
||||||
|
strat_name = "A" if raw == "AStarStrategy" else raw.replace("Strategy", "")
|
||||||
|
return {
|
||||||
|
"лабиринт": maze_name,
|
||||||
|
"стратегия": strat_name,
|
||||||
|
"время_ср": sum(times) / repeats,
|
||||||
|
"длина_пути_ср": sum(path_lens) / repeats,
|
||||||
|
"путь_найден": any(l > 0 for l in path_lens),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
mazes = []
|
||||||
|
|
||||||
|
small = generate_with_walls(10, 10, 0.2)
|
||||||
|
save_maze(small, "maze_small.txt")
|
||||||
|
mazes.append(("маленький 10x10", small))
|
||||||
|
|
||||||
|
medium = generate_with_walls(50, 50, 0.3)
|
||||||
|
save_maze(medium, "maze_medium.txt")
|
||||||
|
mazes.append(("средний 50x50", medium))
|
||||||
|
|
||||||
|
large = generate_with_walls(100, 100, 0.3)
|
||||||
|
save_maze(large, "maze_large.txt")
|
||||||
|
mazes.append(("большой 100x100", large))
|
||||||
|
|
||||||
|
empty = generate_empty(50, 50)
|
||||||
|
save_maze(empty, "maze_empty.txt")
|
||||||
|
mazes.append(("пустой 50x50", empty))
|
||||||
|
|
||||||
|
no_exit = generate_no_exit(20, 20)
|
||||||
|
save_maze(no_exit, "maze_no_exit.txt")
|
||||||
|
mazes.append(("без выхода 20x20", no_exit))
|
||||||
|
|
||||||
|
strategies = [BFSStrategy, DFSStrategy, AStarStrategy]
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for maze_name, maze in mazes:
|
||||||
|
print(maze_name)
|
||||||
|
for strat in strategies:
|
||||||
|
res = run_experiment(maze, strat, maze_name)
|
||||||
|
results.append(res)
|
||||||
|
print(f" {strat.__name__}: {res['время_ср']:.2f} мс")
|
||||||
|
|
||||||
|
csv_path = os.path.join(BASE, "resultslab.csv")
|
||||||
|
with open(csv_path, "w", newline="", encoding="utf-8-sig") as f:
|
||||||
|
writer = csv.DictWriter(
|
||||||
|
f,
|
||||||
|
fieldnames=["лабиринт", "стратегия", "время_ср", "длина_пути_ср", "путь_найден"],
|
||||||
|
delimiter=";",
|
||||||
|
)
|
||||||
|
writer.writeheader()
|
||||||
|
for row in results:
|
||||||
|
row_ru = row.copy()
|
||||||
|
row_ru["путь_найден"] = "да" if row["путь_найден"] else "нет"
|
||||||
|
writer.writerow(row_ru)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
plt.rcParams["font.sans-serif"] = ["Segoe UI", "Arial", "Tahoma", "DejaVu Sans"]
|
||||||
|
plt.rcParams["axes.unicode_minus"] = False
|
||||||
|
|
||||||
|
labyrinths = []
|
||||||
|
for r in results:
|
||||||
|
if r["лабиринт"] not in labyrinths:
|
||||||
|
labyrinths.append(r["лабиринт"])
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(1, len(labyrinths), figsize=(4 * len(labyrinths), 4))
|
||||||
|
if len(labyrinths) == 1:
|
||||||
|
axes = [axes]
|
||||||
|
|
||||||
|
for idx, lab in enumerate(labyrinths):
|
||||||
|
times = []
|
||||||
|
for s in ["BFS", "DFS", "A"]:
|
||||||
|
for r in results:
|
||||||
|
if r["лабиринт"] == lab and r["стратегия"] == s:
|
||||||
|
times.append(r["время_ср"])
|
||||||
|
break
|
||||||
|
axes[idx].bar(["BFS", "DFS", "A"], times, color=["#1a5632", "#0e5fb4", "#e67e22"])
|
||||||
|
axes[idx].set_title(lab)
|
||||||
|
axes[idx].set_ylabel("мс")
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig(os.path.join(BASE, "maze_time_comparison.png"))
|
||||||
|
plt.close()
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
report_path = os.path.join(os.path.dirname(BASE), "report.md")
|
||||||
|
with open(report_path, "w", encoding="utf-8-sig") as f:
|
||||||
|
f.write("# Отчёт: поиск пути в лабиринте\n\n")
|
||||||
|
f.write("Паттерны: Builder, Strategy, Observer\n\n")
|
||||||
|
f.write("```mermaid\nclassDiagram\n")
|
||||||
|
f.write("class MazeBuilder\nclass TextFileMazeBuilder\n")
|
||||||
|
f.write("class PathFindingStrategy\nclass BFSStrategy\n")
|
||||||
|
f.write("class DFSStrategy\nclass AStarStrategy\n")
|
||||||
|
f.write("class MazeSolver\nclass Observer\nclass ConsoleView\n")
|
||||||
|
f.write("MazeBuilder <|-- TextFileMazeBuilder\n")
|
||||||
|
f.write("PathFindingStrategy <|-- BFSStrategy\n")
|
||||||
|
f.write("PathFindingStrategy <|-- DFSStrategy\n")
|
||||||
|
f.write("PathFindingStrategy <|-- AStarStrategy\n")
|
||||||
|
f.write("Observer <|-- ConsoleView\n")
|
||||||
|
f.write("MazeSolver --> PathFindingStrategy\n")
|
||||||
|
f.write("```\n\n")
|
||||||
|
f.write("| Лабиринт | Стратегия | Время (мс) | Длина пути | Найден |\n")
|
||||||
|
f.write("| --- | --- | --- | --- | --- |\n")
|
||||||
|
for r in results:
|
||||||
|
found = "да" if r["путь_найден"] else "нет"
|
||||||
|
f.write(
|
||||||
|
f"| {r['лабиринт']} | {r['стратегия']} | {r['время_ср']:.2f} | "
|
||||||
|
f"{r['длина_пути_ср']:.0f} | {found} |\n"
|
||||||
|
)
|
||||||
|
f.write("\n\n\n")
|
||||||
|
f.write("## Выводы\n\n")
|
||||||
|
f.write("- BFS и A* находят кратчайший путь.\n")
|
||||||
|
f.write("- DFS путь может быть длиннее.\n")
|
||||||
|
f.write("- На пустом лабиринте алгоритмы работают быстрее всего.\n")
|
||||||
|
f.write("- Без выхода все стратегии возвращают пустой путь.\n")
|
||||||
|
|
||||||
|
print("Готово:", report_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
50
rybakovaa/lab2/docs/data/maze_empty.txt
Normal file
50
rybakovaa/lab2/docs/data/maze_empty.txt
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
S
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
E
|
||||||
100
rybakovaa/lab2/docs/data/maze_large.txt
Normal file
100
rybakovaa/lab2/docs/data/maze_large.txt
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
S
|
||||||
|
# # # ## # # # # # # # # # ## # # # # ### # ## ####
|
||||||
|
## # # # # # # # # ## # # # ### ## ## # ## # # ####
|
||||||
|
# # # # # # # # # # # # # # # # # # ## # ## # #
|
||||||
|
# # ## # # ## # # # ## # ## # ## # ## ## #
|
||||||
|
### ## # ### ### # # # ## # # # ## ## # ### ## # #
|
||||||
|
### # # # #### ## # # # # # ## # # # # # #
|
||||||
|
### # ## # # ## # # # ## # ## # # #### # # # ### # # # ## #
|
||||||
|
# # ## # # ## # # # # # ##### #### # #### # ## # #
|
||||||
|
## # # # # # # ## # ## # ## # # # ## # # # #
|
||||||
|
# # ## # # ## # # ### # # # # # # # ### # # ##
|
||||||
|
# # # # # ## # # # ## # ## # # ### # # # # # # # ## #
|
||||||
|
# # # ### ## ## ## # # # # # # # # # # # # # ## # ##
|
||||||
|
# ## ## # # # # # # ## ## # ## # # ## # # # ###
|
||||||
|
# ## # # # # ## # # # # # # # # # # # ### # ##
|
||||||
|
# # # # # # # # #### ### # # ## # ## ## # #
|
||||||
|
# ### # # # # # ## # # #### # # # # ### # # # # # # ##
|
||||||
|
# # # # ## # # # # # ## # # # # ## ## ### #
|
||||||
|
# # ### # ## ## # # # # # # # # # # # # #
|
||||||
|
## ## # # # # # ## # ## # # ## ## # ## # # # # #
|
||||||
|
#### ## # ### # # # # ## # # ## # # ## # # # # # # # # ##
|
||||||
|
## # ### # # # ## # # # ## # # # # # # ### ## ## # ###
|
||||||
|
### #### # # # # # ## ## ### ### ## ## # ## # # ### ## # # #
|
||||||
|
# #### ## # # # ### # # # # # # # ## # # ##### # ## # ##
|
||||||
|
## ### # ## ## # # ## # # # ## # # ## # # #### ### ## ## #
|
||||||
|
## ## # # # # # # # # # ## ###### # ## ## ### #### #
|
||||||
|
# # ## # # # ## # # # # ### # ### # # ## # # # # ## #
|
||||||
|
# ## ## # # # # # ## # # ## # # ## # # # #### # # # #
|
||||||
|
# #### # ### # # # # #### # ## # # ## ### # # # # # # ####
|
||||||
|
## # # # # # # # ### # ## # ## # # ## # # # ## ## # #
|
||||||
|
## # # # # # ## # # # # # # # # ## # # # # # # ###
|
||||||
|
# ## # # # # #### # # ## ## ## # ## # # ### # # ## # # # #
|
||||||
|
# # # # # # ## # # ## ### ## # # # ## ## # ## # # ## # ## # ##
|
||||||
|
# # ## ## # ## # ## # ### # ### ## ## ## ## # ## # #
|
||||||
|
# # # # # # ## # ###### # ## # # ## # # ## ##
|
||||||
|
## ## # # # # # ## ## # # # #### ## ## # # # ## ##
|
||||||
|
### # # ## # # # # ## # # # # # # ## # ## ## ##
|
||||||
|
## # # # # # ## # # # # # ### # ## ### ## # ## # # # # # # ##
|
||||||
|
# ### # # # # ### ## # # # ## ## ### # ## # # # #
|
||||||
|
##### ### # # # ###### # # ## # # # # ## # # # # ##### #
|
||||||
|
# # # # # # ## # #### # ### # ## #### # # # # #
|
||||||
|
# ### # # # # # # # # # #### # # ## # # # # # # # # #
|
||||||
|
## # ### # # # # # ## # # # # ### # # # # # # # # ## # # # # #
|
||||||
|
# # # # # # #### # # # ## ### # # # ## # ### # # ###
|
||||||
|
# # ## # ## # # # # # # ## # # # ## # # # # # ## ##
|
||||||
|
# # # # # # # # ## # # # ### # # ###### # # # # ## #
|
||||||
|
# # # # # ## # #### # # ## # ## ## # ## # # ## #
|
||||||
|
### ## # # # ### # # # ## # # ### ### # # # # # ##
|
||||||
|
## # # ### # # # ## # ## # # ####### # # #### # # # # # ##
|
||||||
|
# ## # # # ### # ## # # ## # # # # # ## ## ### #
|
||||||
|
## ### # # ## # # # ## # ## # ## # ## # ## # ###
|
||||||
|
# # # # ## # # # # # # # # ## ##### # ## ### # ### # ### # # # #
|
||||||
|
## # # # # # # ##### ## # ### # ## ### # # # ##
|
||||||
|
## ## ## # # # # # # # # ## # ### ## # # # # #
|
||||||
|
## # # # # # # # ## # #### # # # # # # # # # # # # ###
|
||||||
|
# # # # # # # ## ### # # ##### # # # # # # ## ## # ## # # #
|
||||||
|
# # # # # # # # # # # # ## # # # #
|
||||||
|
# # ## # ## # ## # # ## ##### # # # # # # ## # # #
|
||||||
|
# ### # ## # ### ## # # # # # ## # # ### ## # # # #
|
||||||
|
### # # # ## ## # # # # # #### # ## # # # # ### ##
|
||||||
|
# # ## ## # ## # ## ### # # # # # # # # # ## # # # #
|
||||||
|
## # # ## # # # ## ### # # # # ### # # # # # # # # #
|
||||||
|
# # ## ### ### # ## # # # # # # # # # # # # # # # # ### #
|
||||||
|
# # # ## # # ## # # # # # # # ## #
|
||||||
|
# # # ## ## # # # # # ## ## # # # ## ### # # # # # ###
|
||||||
|
## # ### # # ## # ## # # # # # # # # # # # #
|
||||||
|
# # ## # # # # # # # # # # # # # ## # # # #
|
||||||
|
## # # # # ## # # # ### # # ## # # ## # ### #
|
||||||
|
# ## # # # ## # # ## ## # # # # ## # ## # # # #
|
||||||
|
# # # # # # ## # ## # ### ## # ## # # ## # # # # #
|
||||||
|
### # # # # # # # # ##### # ## # # # # # # # ## # ### # # # #
|
||||||
|
# # ## # # # ## # ## # # ### # # ## # # # # # # # # #
|
||||||
|
# # # ## # ## # # # # # ### ## # # # # # ## #
|
||||||
|
# ### # ## # # ## # # # # ## # ## ## # # #
|
||||||
|
# # # # # # # ### # # # ## # # ### # # #### # #
|
||||||
|
## ### # # # ## # # # # ## # # # # # # # ### # # # # ## # # #
|
||||||
|
# ## # # # # # # # # # # # # # # # # ###
|
||||||
|
# # # # ### # # # # # # # # # # ### # ## # ### # # #
|
||||||
|
# # ## # # # # # # # # ## # ## # # ### # # # # #
|
||||||
|
# # ## ## # # # # # # # # ## # ## # ## # ## ## # # # # #
|
||||||
|
# ## # # ## # ## # # # ### # # ## #### ### # # #
|
||||||
|
## # # # # ### # # ## # # ## # # # ## # # # # # ## # #
|
||||||
|
## # ## # ### # ### # # # # # ## ## # # # # # ### # # #
|
||||||
|
## ### # # # # ### # # # # # # ## # # # ## ## # # ## # # ### ###
|
||||||
|
# ## # # # # ## # # # # # ### # # # # # # # # ###
|
||||||
|
## ### # ## # # ## # # ## # ## # # # ### # # # # ##
|
||||||
|
# # # ## ##### # ## # ## # # # ### # ## # ## # ## ## # # ###
|
||||||
|
# # ## # ### # # # ## ## # # # # # # # # # #
|
||||||
|
# # # ### # # # # # ## # ## # #### ### # # # # # ### # ## # #
|
||||||
|
# # # # # # # # # # ## ## # # ## # # # # ## ## ## # #
|
||||||
|
# # # # # # ## # # # ##### # # ## ## # # ## # ## # #
|
||||||
|
# ### ## ## # #### ### # # ### # ### # # # ## ### #### ## ## ## ###
|
||||||
|
# # # # # # # ## # # # # # # # # # ##
|
||||||
|
# # # # ## # ## # # # # # # # # ## ## # # #
|
||||||
|
# # ## # # # # # ## # # # # ## ## # # # # ##
|
||||||
|
## # # # # # # ## # # ### ## ### # ## #
|
||||||
|
# # # # # # # # # # # # # # ## ## # # # #
|
||||||
|
## # # # ### # ### # # # # # ## ## # # # # # ## # # # #
|
||||||
|
# # ## # # ## # ### # # # # ### # # ## ###
|
||||||
|
# # # # # # # # # ## # # # # # # # ## # # # ## ## E
|
||||||
50
rybakovaa/lab2/docs/data/maze_medium.txt
Normal file
50
rybakovaa/lab2/docs/data/maze_medium.txt
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
S
|
||||||
|
# # # #### # # # #
|
||||||
|
### # # # # # # ## ## ### #
|
||||||
|
# ## # # # # ## # #### #
|
||||||
|
## ## # # # # ## #
|
||||||
|
# ## # # ### ## ## ### ## ## #### #
|
||||||
|
##### # # # # # ## ## # # # ###
|
||||||
|
### ## ## ## # # ## ### #
|
||||||
|
## # ### # ### # ## # # # # ### ###
|
||||||
|
### # # # # ## # ### # #
|
||||||
|
# ## # # # # # # # #
|
||||||
|
# # ### # ## # ## # #
|
||||||
|
# # # ## # # # # # # ###
|
||||||
|
## ### ### ## ## ## # # #
|
||||||
|
# # ## ## # # ## # ## ## ## #
|
||||||
|
# # # # ## # # # ## # # ##
|
||||||
|
# ## # # # ## # #
|
||||||
|
## # # # ## # # # #### # ##### # ## #
|
||||||
|
# # ## # # # # ##### ## # #
|
||||||
|
# # ## ## # # ## # #
|
||||||
|
# # ## ### # # # # # ### #
|
||||||
|
## # # # ### # # ## # ##
|
||||||
|
# # # # ## # # # ## # # ## # ##
|
||||||
|
# # ## ## # #### #
|
||||||
|
# # # ## ### # ## # # #
|
||||||
|
# # ### # # ## # # ### # # ## #
|
||||||
|
# # # ## # ## # # # # # # #
|
||||||
|
# ## # # # # # # # #
|
||||||
|
# ## # #### # # # # ## #
|
||||||
|
# # ## # ### # # # ### ## ## #
|
||||||
|
# # # ### ## # # # # ## #
|
||||||
|
## # # ### ## # # #
|
||||||
|
### # # ## ## # # # #
|
||||||
|
# # # # # # # # # ## # #
|
||||||
|
# # ## ## # # ## # #### # #
|
||||||
|
# # # # ## # # # ## ## ##
|
||||||
|
# # # ## # ### # ###
|
||||||
|
# # ## ## # # # # # ##
|
||||||
|
# # # # # # # # ## # # #
|
||||||
|
### ##### ## ## # # ### # # ## # #
|
||||||
|
## # ### ## # # # #### # #
|
||||||
|
# # # # # # # ## ## ##
|
||||||
|
# # ## # # # ## ### ## ## #
|
||||||
|
## # # ### # # ## ###
|
||||||
|
## # # # # # # # # # ## # # #### ##
|
||||||
|
# # # # # ## # ## ## #
|
||||||
|
## # # # # # # ## # # # ## # #
|
||||||
|
# ## # ### # # ## # # ### ##
|
||||||
|
## # # # ## # # # # #
|
||||||
|
# # # ### # ## # # ## E
|
||||||
20
rybakovaa/lab2/docs/data/maze_no_exit.txt
Normal file
20
rybakovaa/lab2/docs/data/maze_no_exit.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
S
|
||||||
|
# # # # ###
|
||||||
|
# #
|
||||||
|
# # # ## #
|
||||||
|
## # ## #
|
||||||
|
# # ##
|
||||||
|
# ##
|
||||||
|
# ## # #
|
||||||
|
# #
|
||||||
|
## # #
|
||||||
|
### # #
|
||||||
|
# # # # ###
|
||||||
|
# ##
|
||||||
|
# # ## ##
|
||||||
|
# # ####
|
||||||
|
# # # # #
|
||||||
|
# # #
|
||||||
|
# ## ##
|
||||||
|
## ## # # #
|
||||||
|
# # # ##
|
||||||
10
rybakovaa/lab2/docs/data/maze_small.txt
Normal file
10
rybakovaa/lab2/docs/data/maze_small.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
S
|
||||||
|
|
||||||
|
# #
|
||||||
|
## # #
|
||||||
|
## #
|
||||||
|
#
|
||||||
|
# ##
|
||||||
|
|
||||||
|
# # #
|
||||||
|
### E
|
||||||
BIN
rybakovaa/lab2/docs/data/maze_time_comparison.png
Normal file
BIN
rybakovaa/lab2/docs/data/maze_time_comparison.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
16
rybakovaa/lab2/docs/data/resultslab.csv
Normal file
16
rybakovaa/lab2/docs/data/resultslab.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
лабиринт;стратегия;время_ср;длина_пути_ср;путь_найден
|
||||||
|
маленький 10x10;BFS;0.14045997522771358;19.0;да
|
||||||
|
маленький 10x10;DFS;0.08256000000983477;37.0;да
|
||||||
|
маленький 10x10;A;0.2506999997422099;19.0;да
|
||||||
|
средний 50x50;BFS;2.8775800252333283;99.0;да
|
||||||
|
средний 50x50;DFS;1.9064400112256408;283.0;да
|
||||||
|
средний 50x50;A;2.429639990441501;99.0;да
|
||||||
|
большой 100x100;BFS;12.2316999360919;199.0;да
|
||||||
|
большой 100x100;DFS;8.781959977932274;1643.0;да
|
||||||
|
большой 100x100;A;8.597399992868304;199.0;да
|
||||||
|
пустой 50x50;BFS;4.875819990411401;99.0;да
|
||||||
|
пустой 50x50;DFS;3.1325000105425715;1275.0;да
|
||||||
|
пустой 50x50;A;11.547920037992299;99.0;да
|
||||||
|
без выхода 20x20;BFS;0.0002400018274784088;0.0;нет
|
||||||
|
без выхода 20x20;DFS;0.0002400018274784088;0.0;нет
|
||||||
|
без выхода 20x20;A;0.0001600012183189392;0.0;нет
|
||||||
|
49
rybakovaa/lab2/docs/report.md
Normal file
49
rybakovaa/lab2/docs/report.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Отчёт: поиск пути в лабиринте
|
||||||
|
|
||||||
|
Паттерны: Builder, Strategy, Observer
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class MazeBuilder
|
||||||
|
class TextFileMazeBuilder
|
||||||
|
class PathFindingStrategy
|
||||||
|
class BFSStrategy
|
||||||
|
class DFSStrategy
|
||||||
|
class AStarStrategy
|
||||||
|
class MazeSolver
|
||||||
|
class Observer
|
||||||
|
class ConsoleView
|
||||||
|
MazeBuilder <|-- TextFileMazeBuilder
|
||||||
|
PathFindingStrategy <|-- BFSStrategy
|
||||||
|
PathFindingStrategy <|-- DFSStrategy
|
||||||
|
PathFindingStrategy <|-- AStarStrategy
|
||||||
|
Observer <|-- ConsoleView
|
||||||
|
MazeSolver --> PathFindingStrategy
|
||||||
|
```
|
||||||
|
|
||||||
|
| Лабиринт | Стратегия | Время (мс) | Длина пути | Найден |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| маленький 10x10 | BFS | 0.14 | 19 | да |
|
||||||
|
| маленький 10x10 | DFS | 0.08 | 37 | да |
|
||||||
|
| маленький 10x10 | A | 0.25 | 19 | да |
|
||||||
|
| средний 50x50 | BFS | 2.88 | 99 | да |
|
||||||
|
| средний 50x50 | DFS | 1.91 | 283 | да |
|
||||||
|
| средний 50x50 | A | 2.43 | 99 | да |
|
||||||
|
| большой 100x100 | BFS | 12.23 | 199 | да |
|
||||||
|
| большой 100x100 | DFS | 8.78 | 1643 | да |
|
||||||
|
| большой 100x100 | A | 8.60 | 199 | да |
|
||||||
|
| пустой 50x50 | BFS | 4.88 | 99 | да |
|
||||||
|
| пустой 50x50 | DFS | 3.13 | 1275 | да |
|
||||||
|
| пустой 50x50 | A | 11.55 | 99 | да |
|
||||||
|
| без выхода 20x20 | BFS | 0.00 | 0 | нет |
|
||||||
|
| без выхода 20x20 | DFS | 0.00 | 0 | нет |
|
||||||
|
| без выхода 20x20 | A | 0.00 | 0 | нет |
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Выводы
|
||||||
|
|
||||||
|
- BFS и A* находят кратчайший путь.
|
||||||
|
- DFS путь может быть длиннее.
|
||||||
|
- На пустом лабиринте алгоритмы работают быстрее всего.
|
||||||
|
- Без выхода все стратегии возвращают пустой путь.
|
||||||
Loading…
Reference in New Issue
Block a user