Merge pull request '[1,2] лабораторная работа 1 и 2' (#369) from petryaninyas/2026-rff_mp:task1_2 into develop

Reviewed-on: UNN/2026-rff_mp#369
This commit is contained in:
git_admin 2026-06-26 09:41:20 +00:00
commit 6d882c133a
69 changed files with 1724 additions and 0 deletions

0
petryainiyas/426.md Normal file
View File

118
petryainiyas/task1/bst.py Normal file
View File

@ -0,0 +1,118 @@
from typing import Any, Dict, List, Optional
Node = Dict[str, Any]
def _make_node(name: str, phone: str) -> Node:
return {"name": name, "phone": phone, "left": None, "right": None}
def bst_insert(root: Optional[Node], name: str, phone: str) -> Node:
new_node = _make_node(name, phone)
if root is None:
return new_node
current = root
parent = None
while current is not None:
parent = current
if name < current["name"]:
current = current["left"]
elif name > current["name"]:
current = current["right"]
else:
current["phone"] = phone
return root
if name < parent["name"]:
parent["left"] = new_node
else:
parent["right"] = new_node
return root
def bst_find(root: Optional[Node], name: str) -> Optional[str]:
current = root
while current is not None:
if name < current["name"]:
current = current["left"]
elif name > current["name"]:
current = current["right"]
else:
return current["phone"]
return None
def _find_min_node(node: Node) -> Node:
current = node
while current["left"] is not None:
current = current["left"]
return current
def bst_delete(root: Optional[Node], name: str) -> Optional[Node]:
if root is None:
return None
parent = None
current = root
while current is not None and current["name"] != name:
parent = current
if name < current["name"]:
current = current["left"]
else:
current = current["right"]
if current is None:
return root
if current["left"] is None or current["right"] is None:
child = current["left"] if current["left"] is not None else current["right"]
if parent is None:
return child
if parent["left"] is current:
parent["left"] = child
else:
parent["right"] = child
return root
succ_parent = current
successor = current["right"]
while successor["left"] is not None:
succ_parent = successor
successor = successor["left"]
current["name"] = successor["name"]
current["phone"] = successor["phone"]
successor_child = successor["right"]
if succ_parent["left"] is successor:
succ_parent["left"] = successor_child
else:
succ_parent["right"] = successor_child
return root
def bst_list_all(root: Optional[Node]) -> List[Dict[str, str]]:
result: List[Dict[str, str]] = []
stack: List[Node] = []
current = root
while current is not None or stack:
while current is not None:
stack.append(current)
current = current["left"]
current = stack.pop()
result.append({"name": current["name"], "phone": current["phone"]})
current = current["right"]
return result

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,109 @@
Структура,Режим,Операция,Замер,Время (сек)
LinkedList,случайный,insert,1,4.2622492010
LinkedList,случайный,find,1,0.0314994130
LinkedList,случайный,delete,1,0.0149069000
LinkedList,случайный,insert,2,4.0154580330
LinkedList,случайный,find,2,0.0393284500
LinkedList,случайный,delete,2,0.0210732100
LinkedList,случайный,insert,3,4.0436019780
LinkedList,случайный,find,3,0.0344933660
LinkedList,случайный,delete,3,0.0152639850
LinkedList,случайный,insert,4,3.7182993220
LinkedList,случайный,find,4,0.0327698850
LinkedList,случайный,delete,4,0.0149959540
LinkedList,случайный,insert,5,3.7082228200
LinkedList,случайный,find,5,0.0303762490
LinkedList,случайный,delete,5,0.0141406560
LinkedList,случайный,insert,среднее,3.9495662708
LinkedList,случайный,find,среднее,0.0336934726
LinkedList,случайный,delete,среднее,0.0160761410
HashTable,случайный,insert,1,0.2059865770
HashTable,случайный,find,1,0.0014966100
HashTable,случайный,delete,1,0.0006891700
HashTable,случайный,insert,2,0.2024331460
HashTable,случайный,find,2,0.0015934880
HashTable,случайный,delete,2,0.0007212620
HashTable,случайный,insert,3,0.2126128040
HashTable,случайный,find,3,0.0016566220
HashTable,случайный,delete,3,0.0008358420
HashTable,случайный,insert,4,0.2157934910
HashTable,случайный,find,4,0.0015542810
HashTable,случайный,delete,4,0.0007269120
HashTable,случайный,insert,5,0.2079924580
HashTable,случайный,find,5,0.0013696990
HashTable,случайный,delete,5,0.0006616050
HashTable,случайный,insert,среднее,0.2089636952
HashTable,случайный,find,среднее,0.0015341400
HashTable,случайный,delete,среднее,0.0007269582
BST,случайный,insert,1,0.0166981280
BST,случайный,find,1,0.0001569360
BST,случайный,delete,1,0.0000917280
BST,случайный,insert,2,0.0184119040
BST,случайный,find,2,0.0001517110
BST,случайный,delete,2,0.0001163770
BST,случайный,insert,3,0.0174662270
BST,случайный,find,3,0.0001582930
BST,случайный,delete,3,0.0000892660
BST,случайный,insert,4,0.0191369100
BST,случайный,find,4,0.0002087170
BST,случайный,delete,4,0.0001067050
BST,случайный,insert,5,0.0184276900
BST,случайный,find,5,0.0002767720
BST,случайный,delete,5,0.0001067660
BST,случайный,insert,среднее,0.0180281718
BST,случайный,find,среднее,0.0001904858
BST,случайный,delete,среднее,0.0001021684
LinkedList,отсортированный,insert,1,2.9875078340
LinkedList,отсортированный,find,1,0.0237300610
LinkedList,отсортированный,delete,1,0.0111698260
LinkedList,отсортированный,insert,2,3.0573987940
LinkedList,отсортированный,find,2,0.0243270360
LinkedList,отсортированный,delete,2,0.0115366030
LinkedList,отсортированный,insert,3,2.9641987260
LinkedList,отсортированный,find,3,0.0236313330
LinkedList,отсортированный,delete,3,0.0112848510
LinkedList,отсортированный,insert,4,3.0345914950
LinkedList,отсортированный,find,4,0.0240271220
LinkedList,отсортированный,delete,4,0.0112117310
LinkedList,отсортированный,insert,5,2.9481954700
LinkedList,отсортированный,find,5,0.0239006100
LinkedList,отсортированный,delete,5,0.0110857710
LinkedList,отсортированный,insert,среднее,2.9983784638
LinkedList,отсортированный,find,среднее,0.0239232324
LinkedList,отсортированный,delete,среднее,0.0112577564
HashTable,отсортированный,insert,1,0.1997087560
HashTable,отсортированный,find,1,0.0017550400
HashTable,отсортированный,delete,1,0.0008407980
HashTable,отсортированный,insert,2,0.1968675190
HashTable,отсортированный,find,2,0.0019886760
HashTable,отсортированный,delete,2,0.0008920910
HashTable,отсортированный,insert,3,0.1907563580
HashTable,отсортированный,find,3,0.0018447440
HashTable,отсортированный,delete,3,0.0008684640
HashTable,отсортированный,insert,4,0.2625327630
HashTable,отсортированный,find,4,0.0016053140
HashTable,отсортированный,delete,4,0.0008098670
HashTable,отсортированный,insert,5,0.1936840590
HashTable,отсортированный,find,5,0.0019015160
HashTable,отсортированный,delete,5,0.0009053780
HashTable,отсортированный,insert,среднее,0.2087098910
HashTable,отсортированный,find,среднее,0.0018190580
HashTable,отсортированный,delete,среднее,0.0008633196
BST,отсортированный,insert,1,4.2195800190
BST,отсортированный,find,1,0.0389314570
BST,отсортированный,delete,1,0.0190308920
BST,отсортированный,insert,2,4.1356184250
BST,отсортированный,find,2,0.0383339310
BST,отсортированный,delete,2,0.0194247740
BST,отсортированный,insert,3,4.1204731890
BST,отсортированный,find,3,0.0388593320
BST,отсортированный,delete,3,0.0215428460
BST,отсортированный,insert,4,4.2120902370
BST,отсортированный,find,4,0.0378190250
BST,отсортированный,delete,4,0.0188528460
BST,отсортированный,insert,5,4.1304951260
BST,отсортированный,find,5,0.0359927840
BST,отсортированный,delete,5,0.0179617110
BST,отсортированный,insert,среднее,4.1636513992
BST,отсортированный,find,среднее,0.0379873058
BST,отсортированный,delete,среднее,0.0193626138
1 Структура Режим Операция Замер Время (сек)
2 LinkedList случайный insert 1 4.2622492010
3 LinkedList случайный find 1 0.0314994130
4 LinkedList случайный delete 1 0.0149069000
5 LinkedList случайный insert 2 4.0154580330
6 LinkedList случайный find 2 0.0393284500
7 LinkedList случайный delete 2 0.0210732100
8 LinkedList случайный insert 3 4.0436019780
9 LinkedList случайный find 3 0.0344933660
10 LinkedList случайный delete 3 0.0152639850
11 LinkedList случайный insert 4 3.7182993220
12 LinkedList случайный find 4 0.0327698850
13 LinkedList случайный delete 4 0.0149959540
14 LinkedList случайный insert 5 3.7082228200
15 LinkedList случайный find 5 0.0303762490
16 LinkedList случайный delete 5 0.0141406560
17 LinkedList случайный insert среднее 3.9495662708
18 LinkedList случайный find среднее 0.0336934726
19 LinkedList случайный delete среднее 0.0160761410
20 HashTable случайный insert 1 0.2059865770
21 HashTable случайный find 1 0.0014966100
22 HashTable случайный delete 1 0.0006891700
23 HashTable случайный insert 2 0.2024331460
24 HashTable случайный find 2 0.0015934880
25 HashTable случайный delete 2 0.0007212620
26 HashTable случайный insert 3 0.2126128040
27 HashTable случайный find 3 0.0016566220
28 HashTable случайный delete 3 0.0008358420
29 HashTable случайный insert 4 0.2157934910
30 HashTable случайный find 4 0.0015542810
31 HashTable случайный delete 4 0.0007269120
32 HashTable случайный insert 5 0.2079924580
33 HashTable случайный find 5 0.0013696990
34 HashTable случайный delete 5 0.0006616050
35 HashTable случайный insert среднее 0.2089636952
36 HashTable случайный find среднее 0.0015341400
37 HashTable случайный delete среднее 0.0007269582
38 BST случайный insert 1 0.0166981280
39 BST случайный find 1 0.0001569360
40 BST случайный delete 1 0.0000917280
41 BST случайный insert 2 0.0184119040
42 BST случайный find 2 0.0001517110
43 BST случайный delete 2 0.0001163770
44 BST случайный insert 3 0.0174662270
45 BST случайный find 3 0.0001582930
46 BST случайный delete 3 0.0000892660
47 BST случайный insert 4 0.0191369100
48 BST случайный find 4 0.0002087170
49 BST случайный delete 4 0.0001067050
50 BST случайный insert 5 0.0184276900
51 BST случайный find 5 0.0002767720
52 BST случайный delete 5 0.0001067660
53 BST случайный insert среднее 0.0180281718
54 BST случайный find среднее 0.0001904858
55 BST случайный delete среднее 0.0001021684
56 LinkedList отсортированный insert 1 2.9875078340
57 LinkedList отсортированный find 1 0.0237300610
58 LinkedList отсортированный delete 1 0.0111698260
59 LinkedList отсортированный insert 2 3.0573987940
60 LinkedList отсортированный find 2 0.0243270360
61 LinkedList отсортированный delete 2 0.0115366030
62 LinkedList отсортированный insert 3 2.9641987260
63 LinkedList отсортированный find 3 0.0236313330
64 LinkedList отсортированный delete 3 0.0112848510
65 LinkedList отсортированный insert 4 3.0345914950
66 LinkedList отсортированный find 4 0.0240271220
67 LinkedList отсортированный delete 4 0.0112117310
68 LinkedList отсортированный insert 5 2.9481954700
69 LinkedList отсортированный find 5 0.0239006100
70 LinkedList отсортированный delete 5 0.0110857710
71 LinkedList отсортированный insert среднее 2.9983784638
72 LinkedList отсортированный find среднее 0.0239232324
73 LinkedList отсортированный delete среднее 0.0112577564
74 HashTable отсортированный insert 1 0.1997087560
75 HashTable отсортированный find 1 0.0017550400
76 HashTable отсортированный delete 1 0.0008407980
77 HashTable отсортированный insert 2 0.1968675190
78 HashTable отсортированный find 2 0.0019886760
79 HashTable отсортированный delete 2 0.0008920910
80 HashTable отсортированный insert 3 0.1907563580
81 HashTable отсортированный find 3 0.0018447440
82 HashTable отсортированный delete 3 0.0008684640
83 HashTable отсортированный insert 4 0.2625327630
84 HashTable отсортированный find 4 0.0016053140
85 HashTable отсортированный delete 4 0.0008098670
86 HashTable отсортированный insert 5 0.1936840590
87 HashTable отсортированный find 5 0.0019015160
88 HashTable отсортированный delete 5 0.0009053780
89 HashTable отсортированный insert среднее 0.2087098910
90 HashTable отсортированный find среднее 0.0018190580
91 HashTable отсортированный delete среднее 0.0008633196
92 BST отсортированный insert 1 4.2195800190
93 BST отсортированный find 1 0.0389314570
94 BST отсортированный delete 1 0.0190308920
95 BST отсортированный insert 2 4.1356184250
96 BST отсортированный find 2 0.0383339310
97 BST отсортированный delete 2 0.0194247740
98 BST отсортированный insert 3 4.1204731890
99 BST отсортированный find 3 0.0388593320
100 BST отсортированный delete 3 0.0215428460
101 BST отсортированный insert 4 4.2120902370
102 BST отсортированный find 4 0.0378190250
103 BST отсортированный delete 4 0.0188528460
104 BST отсортированный insert 5 4.1304951260
105 BST отсортированный find 5 0.0359927840
106 BST отсортированный delete 5 0.0179617110
107 BST отсортированный insert среднее 4.1636513992
108 BST отсортированный find среднее 0.0379873058
109 BST отсортированный delete среднее 0.0193626138

Binary file not shown.

View File

@ -0,0 +1,172 @@
from __future__ import annotations
import csv
import random
import time
from pathlib import Path
from typing import Dict, List, Tuple
from linked_list import ll_insert, ll_find, ll_delete
from hash_table import ht_insert, ht_find, ht_delete
from bst import bst_insert, bst_find, bst_delete
from utils import generate_records, prepare_records_variants
Record = Tuple[str, str]
def make_missing_names(count: int = 10) -> List[str]:
return [f"None_{i}" for i in range(count)]
def pick_existing_names(records: List[Record], count: int, seed: int = 42) -> List[str]:
rng = random.Random(seed)
unique_names = list(dict.fromkeys(name for name, _ in records))
if len(unique_names) < count:
raise ValueError(f"Not enough unique names: need {count}, got {len(unique_names)}")
return rng.sample(unique_names, count)
def pick_delete_names(records: List[Record], count: int = 50, seed: int = 43) -> List[str]:
rng = random.Random(seed)
unique_names = list(dict.fromkeys(name for name, _ in records))
if len(unique_names) < count:
raise ValueError(f"Not enough unique names: need {count}, got {len(unique_names)}")
return rng.sample(unique_names, count)
def build_structure(structure_name: str, records: List[Record], buckets_count: int = 2048):
if structure_name == "linked_list":
structure = None
for name, phone in records:
structure = ll_insert(structure, name, phone)
return structure
if structure_name == "hash_table":
buckets = [None] * buckets_count
for name, phone in records:
buckets = ht_insert(buckets, name, phone)
return buckets
if structure_name == "bst":
root = None
for name, phone in records:
root = bst_insert(root, name, phone)
return root
raise ValueError(f"Unknown structure: {structure_name}")
def do_find(structure_name: str, structure: object, existing_names: List[str], missing_names: List[str]) -> None:
if structure_name == "linked_list":
for name in existing_names:
ll_find(structure, name)
for name in missing_names:
ll_find(structure, name)
return
if structure_name == "hash_table":
for name in existing_names:
ht_find(structure, name)
for name in missing_names:
ht_find(structure, name)
return
if structure_name == "bst":
for name in existing_names:
bst_find(structure, name)
for name in missing_names:
bst_find(structure, name)
return
raise ValueError(f"Unknown structure: {structure_name}")
def do_delete(structure_name: str, structure: object, delete_names: List[str]):
if structure_name == "linked_list":
for name in delete_names:
structure = ll_delete(structure, name)
return structure
if structure_name == "hash_table":
for name in delete_names:
structure = ht_delete(structure, name)
return structure
if structure_name == "bst":
for name in delete_names:
structure = bst_delete(structure, name)
return structure
raise ValueError(f"Unknown structure: {structure_name}")
def measure_once(structure_name: str, records: List[Record], buckets_count: int = 2048) -> Dict[str, float]:
existing_names = pick_existing_names(records, 100, seed=42)
missing_names = make_missing_names(10)
delete_names = pick_delete_names(records, 50, seed=43)
start = time.perf_counter()
structure = build_structure(structure_name, records, buckets_count=buckets_count)
insert_time = time.perf_counter() - start
start = time.perf_counter()
do_find(structure_name, structure, existing_names, missing_names)
find_time = time.perf_counter() - start
start = time.perf_counter()
structure = do_delete(structure_name, structure, delete_names)
delete_time = time.perf_counter() - start
return {"insert": insert_time, "find": find_time, "delete": delete_time}
def run_experiments(n: int = 10000, buckets_count: int = 2048, repeats: int = 5):
records = generate_records(n, repeat_names=False)
records_shuffled, records_sorted = prepare_records_variants(records)
datasets = [
("случайный", records_shuffled),
("отсортированный", records_sorted),
]
structures = [
("LinkedList", "linked_list"),
("HashTable", "hash_table"),
("BST", "bst"),
]
operations = ("insert", "find", "delete")
rows = [["Структура", "Режим", "Операция", "Замер", "Время (сек)"]]
for mode_name, dataset_records in datasets:
for human_name, structure_name in structures:
times_by_op = {op: [] for op in operations}
for attempt in range(1, repeats + 1):
result = measure_once(structure_name, dataset_records, buckets_count=buckets_count)
for op_name in operations:
elapsed = result[op_name]
times_by_op[op_name].append(elapsed)
rows.append([human_name, mode_name, op_name, attempt, f"{elapsed:.10f}"])
for op_name in operations:
avg_time = sum(times_by_op[op_name]) / len(times_by_op[op_name])
rows.append([human_name, mode_name, op_name, "среднее", f"{avg_time:.10f}"])
return rows
def save_results_csv(rows, filename: str = "results.csv"):
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(rows)
def main():
rows = run_experiments(n=10000, buckets_count=2048, repeats=5)
save_results_csv(rows, "results.csv")
print("Saved results.csv")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,44 @@
from typing import Any, Dict, List, Optional
from linked_list import ll_insert, ll_find, ll_delete, ll_list_all
Bucket = Optional[Dict[str, Any]]
def _hash_name(name: str, buckets_count: int) -> int:
if buckets_count <= 0:
return 0
return sum(ord(ch) for ch in name) % buckets_count
def ht_insert(buckets: List[Bucket], name: str, phone: str) -> List[Bucket]:
if not buckets:
return buckets
index = _hash_name(name, len(buckets))
buckets[index] = ll_insert(buckets[index], name, phone)
return buckets
def ht_find(buckets: List[Bucket], name: str) -> Optional[str]:
if not buckets:
return None
index = _hash_name(name, len(buckets))
return ll_find(buckets[index], name)
def ht_delete(buckets: List[Bucket], name: str) -> List[Bucket]:
if not buckets:
return buckets
index = _hash_name(name, len(buckets))
buckets[index] = ll_delete(buckets[index], name)
return buckets
def ht_list_all(buckets: List[Bucket]) -> List[Dict[str, str]]:
records: List[Dict[str, str]] = []
for head in buckets:
records.extend(ll_list_all(head))
return sorted(records, key=lambda x: x["name"])

View File

@ -0,0 +1,73 @@
from typing import Any, Dict, List, Optional
Node = Dict[str, Any]
def _make_node(name: str, phone: str) -> Node:
return {"name": name, "phone": phone, "next": None}
def sort_records(records: List[Dict[str, str]]) -> List[Dict[str, str]]:
return sorted(records, key=lambda x: x["name"])
def ll_insert(head: Optional[Node], name: str, phone: str) -> Node:
new_node = _make_node(name, phone)
if head is None:
return new_node
current = head
while current is not None:
if current["name"] == name:
current["phone"] = phone
return head
if current["next"] is None:
current["next"] = new_node
return head
current = current["next"]
return head
def ll_find(head: Optional[Node], name: str) -> Optional[str]:
current = head
while current is not None:
if current["name"] == name:
return current["phone"]
current = current["next"]
return None
def ll_delete(head: Optional[Node], name: str) -> Optional[Node]:
if head is None:
return None
if head["name"] == name:
return head["next"]
prev = head
current = head["next"]
while current is not None:
if current["name"] == name:
prev["next"] = current["next"]
return head
prev = current
current = current["next"]
return head
def ll_list_all(head: Optional[Node]) -> List[Dict[str, str]]:
records: List[Dict[str, str]] = []
current = head
while current is not None:
records.append({"name": current["name"], "phone": current["phone"]})
current = current["next"]
return sort_records(records)

View File

@ -0,0 +1,21 @@
from __future__ import annotations
import csv
from pathlib import Path
from experiments import run_experiments, save_results_csv
from plot_results import build_graphs, load_average_results
def main():
rows = run_experiments(n=10000, buckets_count=2048, repeats=5)
save_results_csv(rows, "results.csv")
averaged = load_average_results("results.csv")
build_graphs(averaged, output_dir="docs/data")
print("Done.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,66 @@
from __future__ import annotations
import csv
from collections import defaultdict
from pathlib import Path
import matplotlib.pyplot as plt
from typing import List, Dict, Any
# Карта соответствия заголовков CSV для удобства поддержки
_CSV_KEYS = {
"STRUCTURE": "Структура",
"MODE": "Режим",
"OP": "Операция",
"MEASURE": "Замер",
"TIME": "Время (сек)"
}
def load_average_results(csv_file: str) -> List[Dict[str, Any]]:
"""Загружает только строки со средними значениями из CSV."""
with open(csv_file, "r", encoding="utf-8") as fh:
return [
{
"structure": row[_CSV_KEYS["STRUCTURE"]],
"mode": row[_CSV_KEYS["MODE"]],
"operation": row[_CSV_KEYS["OP"]],
"time": float(row[_CSV_KEYS["TIME"]])
}
for row in csv.DictReader(fh)
if row[_CSV_KEYS["MEASURE"]] == "среднее"
]
def _render_chart(operation: str, data: List[Dict[str, Any]], save_path: Path) -> None:
"""Вспомогательная функция для отрисовки одного столбчатого графика."""
fig, ax = plt.subplots(figsize=(10, 5))
labels = [f"{rec['structure']}\n{rec['mode']}" for rec in data]
values = [rec["time"] for rec in data]
ax.bar(labels, values, color="cornflowerblue", edgecolor="navy")
ax.set_title(f"{operation.capitalize()} Performance Comparison")
ax.set_xlabel("Data Structure & Input Order")
ax.set_ylabel("Execution Time (seconds)")
ax.tick_params(axis="x", rotation=15)
fig.tight_layout()
fig.savefig(save_path, dpi=150)
plt.close(fig)
print(f"Saved chart: {save_path}")
def build_graphs(results: List[Dict[str, Any]], output_dir: str = "docs/data") -> None:
"""Группирует результаты по операциям и сохраняет графики."""
out_path = Path(output_dir)
out_path.mkdir(parents=True, exist_ok=True)
grouped = defaultdict(list)
for record in results:
grouped[record["operation"]].append(record)
for op_name in ("insert", "find", "delete"):
if op_name in grouped:
target_file = out_path / f"{op_name}.png"
_render_chart(op_name, grouped[op_name], target_file)
def main():
avg_data = load_average_results("results.csv")
build_graphs(avg_data)
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
matplotlib>=3.8

View File

@ -0,0 +1,109 @@
Структура,Режим,Операция,Замер,Время (сек)
LinkedList,случайный,insert,1,6.8132659000
LinkedList,случайный,find,1,0.0553455000
LinkedList,случайный,delete,1,0.0304272000
LinkedList,случайный,insert,2,6.8263299000
LinkedList,случайный,find,2,0.0562645000
LinkedList,случайный,delete,2,0.0303858000
LinkedList,случайный,insert,3,6.8117682000
LinkedList,случайный,find,3,0.0567219000
LinkedList,случайный,delete,3,0.0302671000
LinkedList,случайный,insert,4,7.0393228000
LinkedList,случайный,find,4,0.0544359000
LinkedList,случайный,delete,4,0.0313359000
LinkedList,случайный,insert,5,7.0633509000
LinkedList,случайный,find,5,0.0552176000
LinkedList,случайный,delete,5,0.0318664000
LinkedList,случайный,insert,среднее,6.9108075400
LinkedList,случайный,find,среднее,0.0555970800
LinkedList,случайный,delete,среднее,0.0308564800
HashTable,случайный,insert,1,0.3898307000
HashTable,случайный,find,1,0.0022226000
HashTable,случайный,delete,1,0.0012723000
HashTable,случайный,insert,2,0.3844561000
HashTable,случайный,find,2,0.0024825000
HashTable,случайный,delete,2,0.0013425000
HashTable,случайный,insert,3,0.3740853000
HashTable,случайный,find,3,0.0026141000
HashTable,случайный,delete,3,0.0013710000
HashTable,случайный,insert,4,0.3870099000
HashTable,случайный,find,4,0.0022063000
HashTable,случайный,delete,4,0.0015940000
HashTable,случайный,insert,5,0.3869087000
HashTable,случайный,find,5,0.0022110000
HashTable,случайный,delete,5,0.0012524000
HashTable,случайный,insert,среднее,0.3844581400
HashTable,случайный,find,среднее,0.0023473000
HashTable,случайный,delete,среднее,0.0013664400
BST,случайный,insert,1,0.0375842000
BST,случайный,find,1,0.0003637000
BST,случайный,delete,1,0.0001893000
BST,случайный,insert,2,0.0364385000
BST,случайный,find,2,0.0003284000
BST,случайный,delete,2,0.0002408000
BST,случайный,insert,3,0.0359604000
BST,случайный,find,3,0.0003705000
BST,случайный,delete,3,0.0001883000
BST,случайный,insert,4,0.0371920000
BST,случайный,find,4,0.0003347000
BST,случайный,delete,4,0.0001711000
BST,случайный,insert,5,0.0385580000
BST,случайный,find,5,0.0003628000
BST,случайный,delete,5,0.0001828000
BST,случайный,insert,среднее,0.0371466200
BST,случайный,find,среднее,0.0003520200
BST,случайный,delete,среднее,0.0001944600
LinkedList,отсортированный,insert,1,6.7166487000
LinkedList,отсортированный,find,1,0.0614362000
LinkedList,отсортированный,delete,1,0.0324487000
LinkedList,отсортированный,insert,2,6.8582294000
LinkedList,отсортированный,find,2,0.0588546000
LinkedList,отсортированный,delete,2,0.0332572000
LinkedList,отсортированный,insert,3,6.6991410000
LinkedList,отсортированный,find,3,0.0521304000
LinkedList,отсортированный,delete,3,0.0296734000
LinkedList,отсортированный,insert,4,6.7166336000
LinkedList,отсортированный,find,4,0.0521848000
LinkedList,отсортированный,delete,4,0.0286064000
LinkedList,отсортированный,insert,5,6.6510829000
LinkedList,отсортированный,find,5,0.0547075000
LinkedList,отсортированный,delete,5,0.0285625000
LinkedList,отсортированный,insert,среднее,6.7283471200
LinkedList,отсортированный,find,среднее,0.0558627000
LinkedList,отсортированный,delete,среднее,0.0305096400
HashTable,отсортированный,insert,1,0.3645209000
HashTable,отсортированный,find,1,0.0032770000
HashTable,отсортированный,delete,1,0.0018631000
HashTable,отсортированный,insert,2,0.3579217000
HashTable,отсортированный,find,2,0.0032143000
HashTable,отсортированный,delete,2,0.0016427000
HashTable,отсортированный,insert,3,0.3684879000
HashTable,отсортированный,find,3,0.0026412000
HashTable,отсортированный,delete,3,0.0014870000
HashTable,отсортированный,insert,4,0.3873619000
HashTable,отсортированный,find,4,0.0028024000
HashTable,отсортированный,delete,4,0.0015786000
HashTable,отсортированный,insert,5,0.3642992000
HashTable,отсортированный,find,5,0.0031264000
HashTable,отсортированный,delete,5,0.0015886000
HashTable,отсортированный,insert,среднее,0.3685183200
HashTable,отсортированный,find,среднее,0.0030122600
HashTable,отсортированный,delete,среднее,0.0016320000
BST,отсортированный,insert,1,10.5552378000
BST,отсортированный,find,1,0.1016856000
BST,отсортированный,delete,1,0.0422728000
BST,отсортированный,insert,2,10.3035871000
BST,отсортированный,find,2,0.1008642000
BST,отсортированный,delete,2,0.0450330000
BST,отсортированный,insert,3,10.6304005000
BST,отсортированный,find,3,0.1073470000
BST,отсортированный,delete,3,0.0816121000
BST,отсортированный,insert,4,10.3183078000
BST,отсортированный,find,4,0.1005074000
BST,отсортированный,delete,4,0.0422195000
BST,отсортированный,insert,5,10.3131368000
BST,отсортированный,find,5,0.1001096000
BST,отсортированный,delete,5,0.0416660000
BST,отсортированный,insert,среднее,10.4241340000
BST,отсортированный,find,среднее,0.1021027600
BST,отсортированный,delete,среднее,0.0505606800
1 Структура Режим Операция Замер Время (сек)
2 LinkedList случайный insert 1 6.8132659000
3 LinkedList случайный find 1 0.0553455000
4 LinkedList случайный delete 1 0.0304272000
5 LinkedList случайный insert 2 6.8263299000
6 LinkedList случайный find 2 0.0562645000
7 LinkedList случайный delete 2 0.0303858000
8 LinkedList случайный insert 3 6.8117682000
9 LinkedList случайный find 3 0.0567219000
10 LinkedList случайный delete 3 0.0302671000
11 LinkedList случайный insert 4 7.0393228000
12 LinkedList случайный find 4 0.0544359000
13 LinkedList случайный delete 4 0.0313359000
14 LinkedList случайный insert 5 7.0633509000
15 LinkedList случайный find 5 0.0552176000
16 LinkedList случайный delete 5 0.0318664000
17 LinkedList случайный insert среднее 6.9108075400
18 LinkedList случайный find среднее 0.0555970800
19 LinkedList случайный delete среднее 0.0308564800
20 HashTable случайный insert 1 0.3898307000
21 HashTable случайный find 1 0.0022226000
22 HashTable случайный delete 1 0.0012723000
23 HashTable случайный insert 2 0.3844561000
24 HashTable случайный find 2 0.0024825000
25 HashTable случайный delete 2 0.0013425000
26 HashTable случайный insert 3 0.3740853000
27 HashTable случайный find 3 0.0026141000
28 HashTable случайный delete 3 0.0013710000
29 HashTable случайный insert 4 0.3870099000
30 HashTable случайный find 4 0.0022063000
31 HashTable случайный delete 4 0.0015940000
32 HashTable случайный insert 5 0.3869087000
33 HashTable случайный find 5 0.0022110000
34 HashTable случайный delete 5 0.0012524000
35 HashTable случайный insert среднее 0.3844581400
36 HashTable случайный find среднее 0.0023473000
37 HashTable случайный delete среднее 0.0013664400
38 BST случайный insert 1 0.0375842000
39 BST случайный find 1 0.0003637000
40 BST случайный delete 1 0.0001893000
41 BST случайный insert 2 0.0364385000
42 BST случайный find 2 0.0003284000
43 BST случайный delete 2 0.0002408000
44 BST случайный insert 3 0.0359604000
45 BST случайный find 3 0.0003705000
46 BST случайный delete 3 0.0001883000
47 BST случайный insert 4 0.0371920000
48 BST случайный find 4 0.0003347000
49 BST случайный delete 4 0.0001711000
50 BST случайный insert 5 0.0385580000
51 BST случайный find 5 0.0003628000
52 BST случайный delete 5 0.0001828000
53 BST случайный insert среднее 0.0371466200
54 BST случайный find среднее 0.0003520200
55 BST случайный delete среднее 0.0001944600
56 LinkedList отсортированный insert 1 6.7166487000
57 LinkedList отсортированный find 1 0.0614362000
58 LinkedList отсортированный delete 1 0.0324487000
59 LinkedList отсортированный insert 2 6.8582294000
60 LinkedList отсортированный find 2 0.0588546000
61 LinkedList отсортированный delete 2 0.0332572000
62 LinkedList отсортированный insert 3 6.6991410000
63 LinkedList отсортированный find 3 0.0521304000
64 LinkedList отсортированный delete 3 0.0296734000
65 LinkedList отсортированный insert 4 6.7166336000
66 LinkedList отсортированный find 4 0.0521848000
67 LinkedList отсортированный delete 4 0.0286064000
68 LinkedList отсортированный insert 5 6.6510829000
69 LinkedList отсортированный find 5 0.0547075000
70 LinkedList отсортированный delete 5 0.0285625000
71 LinkedList отсортированный insert среднее 6.7283471200
72 LinkedList отсортированный find среднее 0.0558627000
73 LinkedList отсортированный delete среднее 0.0305096400
74 HashTable отсортированный insert 1 0.3645209000
75 HashTable отсортированный find 1 0.0032770000
76 HashTable отсортированный delete 1 0.0018631000
77 HashTable отсортированный insert 2 0.3579217000
78 HashTable отсортированный find 2 0.0032143000
79 HashTable отсортированный delete 2 0.0016427000
80 HashTable отсортированный insert 3 0.3684879000
81 HashTable отсортированный find 3 0.0026412000
82 HashTable отсортированный delete 3 0.0014870000
83 HashTable отсортированный insert 4 0.3873619000
84 HashTable отсортированный find 4 0.0028024000
85 HashTable отсортированный delete 4 0.0015786000
86 HashTable отсортированный insert 5 0.3642992000
87 HashTable отсортированный find 5 0.0031264000
88 HashTable отсортированный delete 5 0.0015886000
89 HashTable отсортированный insert среднее 0.3685183200
90 HashTable отсортированный find среднее 0.0030122600
91 HashTable отсортированный delete среднее 0.0016320000
92 BST отсортированный insert 1 10.5552378000
93 BST отсортированный find 1 0.1016856000
94 BST отсортированный delete 1 0.0422728000
95 BST отсортированный insert 2 10.3035871000
96 BST отсортированный find 2 0.1008642000
97 BST отсортированный delete 2 0.0450330000
98 BST отсортированный insert 3 10.6304005000
99 BST отсортированный find 3 0.1073470000
100 BST отсортированный delete 3 0.0816121000
101 BST отсортированный insert 4 10.3183078000
102 BST отсортированный find 4 0.1005074000
103 BST отсортированный delete 4 0.0422195000
104 BST отсортированный insert 5 10.3131368000
105 BST отсортированный find 5 0.1001096000
106 BST отсортированный delete 5 0.0416660000
107 BST отсортированный insert среднее 10.4241340000
108 BST отсортированный find среднее 0.1021027600
109 BST отсортированный delete среднее 0.0505606800

View File

@ -0,0 +1,29 @@
import random
from typing import List, Tuple
Record = Tuple[str, str]
NAME_POOL = (
"User_Alex", "User_Bob", "User_Cat", "User_Dan", "User_Eva",
"User_Fox", "User_Geo", "User_Hen", "User_Ira", "User_Leo"
)
def generate_records(n: int, repeat_names: bool = False, seed: int = 42) -> List[Record]:
"""Генерирует n кортежей (имя, телефон)."""
rng = random.Random(seed)
if repeat_names:
return [
(rng.choice(NAME_POOL), str(rng.randint(10**9, 10**10 - 1)))
for _ in range(n)
]
return [
(f"User_{i:05d}", str(10**9 + i))
for i in range(n)
]
def prepare_records_variants(records: List[Record], seed: int = 42) -> Tuple[List[Record], List[Record]]:
"""Возвращает пару: (перемешанный список, отсортированный по имени список)."""
shuffled = records.copy()
random.Random(seed).shuffle(shuffled)
sorted_records = sorted(records, key=lambda rec: rec[0])
return shuffled, sorted_records

View File

View File

@ -0,0 +1,7 @@
from abc import ABC, abstractmethod
class MazeBuilder(ABC):
@abstractmethod
def buildFromFile(self, filename):
raise NotImplementedError

View File

@ -0,0 +1,52 @@
from core.cell import Cell
from core.maze import Maze
from builders.maze_builder import MazeBuilder
class TextFileMazeBuilder(MazeBuilder):
def buildFromFile(self, filename):
with open(filename, "r", encoding="utf-8") as f:
lines = [line.rstrip("\n") for line in f]
if not lines:
raise ValueError("Maze file is empty")
width = max(len(line) for line in lines)
height = len(lines)
cells = []
startCell = None
exitCell = None
for y, line in enumerate(lines):
row = []
for x in range(width):
ch = line[x] if x < len(line) else "#"
if ch == "#":
cell = Cell(x, y, isWall=True)
elif ch == "S":
if startCell is not None:
raise ValueError("Multiple start cells found")
cell = Cell(x, y, isWall=False, isStart=True)
startCell = cell
elif ch == "E":
if exitCell is not None:
raise ValueError("Multiple exit cells found")
cell = Cell(x, y, isWall=False, isExit=True)
exitCell = cell
elif ch in (" ", "."):
cell = Cell(x, y, isWall=False)
elif ch.isdigit():
cell = Cell(x, y, isWall=False, weight=max(1, int(ch)))
else:
raise ValueError(f"Unsupported symbol '{ch}' at ({x}, {y})")
row.append(cell)
cells.append(row)
if startCell is None:
raise ValueError("Start cell 'S' not found")
if exitCell is None:
raise ValueError("Exit cell 'E' not found")
return Maze(cells, width, height, startCell, exitCell)

View File

View File

@ -0,0 +1,11 @@
from abc import ABC, abstractmethod
class Command(ABC):
@abstractmethod
def execute(self):
raise NotImplementedError
@abstractmethod
def undo(self):
raise NotImplementedError

View File

@ -0,0 +1,37 @@
from commands.command import Command
class MoveCommand(Command):
DIRECTION_TO_DELTA = {
"W": (0, -1),
"A": (-1, 0),
"S": (0, 1),
"D": (1, 0),
}
def __init__(self, player, maze, direction):
self.player = player
self.maze = maze
self.direction = direction.upper()
self.previousCell = None
def execute(self):
if self.direction not in self.DIRECTION_TO_DELTA:
return False
dx, dy = self.DIRECTION_TO_DELTA[self.direction]
current = self.player.currentCell
new_cell = self.maze.getCell(current.x + dx, current.y + dy)
if new_cell is None or not new_cell.isPassable():
return False
self.previousCell = current
self.player.setCell(new_cell)
return True
def undo(self):
if self.previousCell is None:
return False
self.player.setCell(self.previousCell)
return True

View File

@ -0,0 +1,30 @@
from commands.move_command import MoveCommand
class GameController:
def __init__(self, maze, player, view):
self.maze = maze
self.player = player
self.view = view
self.history = []
def move(self, direction):
command = MoveCommand(self.player, self.maze, direction)
if command.execute():
self.history.append(command)
self.view.update({"type": "move", "direction": direction})
self.view.render(self.maze, player_position=self.player.currentCell)
return True
print("Cannot move there")
return False
def undo(self):
if not self.history:
print("Nothing to undo")
return False
command = self.history.pop()
if command.undo():
self.view.update({"type": "undo"})
self.view.render(self.maze, player_position=self.player.currentCell)
return True
return False

View File

View File

@ -0,0 +1,26 @@
from dataclasses import dataclass
@dataclass
class Cell:
x: int
y: int
isWall: bool = False
isStart: bool = False
isExit: bool = False
weight: int = 1
def isPassable(self):
return not self.isWall
def __repr__(self):
parts = [f"Cell({self.x}, {self.y}"]
if self.isWall:
parts.append("WALL")
if self.isStart:
parts.append("START")
if self.isExit:
parts.append("EXIT")
if self.weight != 1:
parts.append(f"w={self.weight}")
return ", ".join(parts) + ")"

View File

@ -0,0 +1,49 @@
class Maze:
def __init__(self, cells, width, height, startCell=None, exitCell=None):
self.cells = cells
self.width = width
self.height = height
self.startCell = startCell
self.exitCell = exitCell
def getCell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[y][x]
return None
def getNeighbors(self, cell):
neighbors = []
for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)):
nx, ny = cell.x + dx, cell.y + dy
neighbor = self.getCell(nx, ny)
if neighbor is not None and neighbor.isPassable():
neighbors.append(neighbor)
return neighbors
def render_lines(self, player_position=None, path=None):
path_set = {(c.x, c.y) for c in path} if path else set()
player_pos = None if player_position is None else (player_position.x, player_position.y)
lines = []
for y in range(self.height):
row = []
for x in range(self.width):
cell = self.cells[y][x]
if player_pos == (x, y):
row.append("P")
elif cell.isStart:
row.append("S")
elif cell.isExit:
row.append("E")
elif cell.isWall:
row.append("#")
elif (x, y) in path_set:
row.append("*")
elif cell.weight > 1:
row.append(str(cell.weight))
else:
row.append(" ")
lines.append("".join(row))
return lines
def render(self, player_position=None, path=None):
return "\n".join(self.render_lines(player_position=player_position, path=path))

View File

@ -0,0 +1,6 @@
class Player:
def __init__(self, currentCell):
self.currentCell = currentCell
def setCell(self, cell):
self.currentCell = cell

View File

@ -0,0 +1,11 @@
from dataclasses import dataclass, field
@dataclass
class SearchStats:
timeMs: float
visitedCells: int
pathLength: int
path: list = field(default_factory=list)
found: bool = False
algorithm: str = ""

Binary file not shown.

View File

@ -0,0 +1,225 @@
from pathlib import Path
from statistics import mean
import csv
import random
import matplotlib.pyplot as plt
from core.cell import Cell
from core.maze import Maze
from solver.maze_solver import MazeSolver
from strategies.astar_strategy import AStarStrategy
from strategies.bfs_strategy import BFSStrategy
from strategies.dfs_strategy import DFSStrategy
from strategies.dijkstra_strategy import DijkstraStrategy
BASE_DIR = Path(__file__).resolve().parent
OUT_DIR = BASE_DIR / "experiment_results"
def build_maze_from_symbols(lines):
height = len(lines)
width = max(len(line) for line in lines)
cells = []
start = None
exit_cell = None
for y, line in enumerate(lines):
row = []
for x in range(width):
ch = line[x] if x < len(line) else "#"
if ch == "#":
cell = Cell(x, y, isWall=True)
elif ch == "S":
cell = Cell(x, y, isWall=False, isStart=True)
start = cell
elif ch == "E":
cell = Cell(x, y, isWall=False, isExit=True)
exit_cell = cell
elif ch == " " or ch == ".":
cell = Cell(x, y, isWall=False)
elif ch.isdigit():
cell = Cell(x, y, isWall=False, weight=int(ch))
else:
raise ValueError(f"Unknown symbol '{ch}' at {x},{y}")
row.append(cell)
cells.append(row)
return Maze(cells, width, height, start, exit_cell)
def generate_empty_maze(width, height):
lines = [" " * width for _ in range(height)]
lines = [list(row) for row in lines]
lines[1][1] = "S"
lines[height - 2][width - 2] = "E"
return build_maze_from_symbols(["".join(row) for row in lines])
def generate_simple_maze(width, height):
grid = [["#" for _ in range(width)] for _ in range(height)]
for x in range(1, width - 1):
grid[1][x] = " "
for y in range(1, height - 1):
grid[y][width - 2] = " "
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return build_maze_from_symbols(["".join(row) for row in grid])
def generate_branching_maze(width, height, seed=42, wall_density=0.30):
rng = random.Random(seed)
grid = [["#" for _ in range(width)] for _ in range(height)]
x, y = 1, 1
grid[y][x] = "S"
while (x, y) != (width - 2, height - 2):
candidates = []
for dx, dy in [(1, 0), (0, 1)]:
nx, ny = x + dx, y + dy
if 1 <= nx < width - 1 and 1 <= ny < height - 1:
candidates.append((nx, ny))
if not candidates:
break
x, y = rng.choice(candidates)
grid[y][x] = " "
grid[height - 2][width - 2] = "E"
# carve extra corridors and dead ends
for yy in range(1, height - 1):
for xx in range(1, width - 1):
if grid[yy][xx] == "#" and rng.random() > wall_density:
grid[yy][xx] = " "
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return build_maze_from_symbols(["".join(row) for row in grid])
def generate_no_path_maze(width, height):
grid = [[" " for _ in range(width)] for _ in range(height)]
for x in range(width):
grid[height // 2][x] = "#"
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return build_maze_from_symbols(["".join(row) for row in grid])
def generate_weighted_maze(width, height, seed=123):
rng = random.Random(seed)
grid = [[" " for _ in range(width)] for _ in range(height)]
for y in range(height):
for x in range(width):
r = rng.random()
if r < 0.12:
grid[y][x] = "#"
elif r < 0.25:
grid[y][x] = "3"
elif r < 0.40:
grid[y][x] = "2"
else:
grid[y][x] = "1"
# ensure path-ish
for x in range(width):
grid[1][x] = "1"
for y in range(1, height):
grid[y][width - 2] = "1"
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return build_maze_from_symbols(["".join(row) for row in grid])
def bench_one_maze(maze_name, maze, strategies, repeats=5):
summary_rows = []
raw_rows = []
for strategy_name, strategy_factory in strategies:
times, visiteds, lengths = [], [], []
for run in range(1, repeats + 1):
solver = MazeSolver(maze)
solver.setStrategy(strategy_factory())
stats = solver.solve()
raw_rows.append([maze_name, strategy_name, run, f"{stats.timeMs:.6f}", stats.visitedCells, stats.pathLength])
times.append(stats.timeMs)
visiteds.append(stats.visitedCells)
lengths.append(stats.pathLength)
summary_rows.append([maze_name, strategy_name, f"{mean(times):.6f}", f"{mean(visiteds):.2f}", f"{mean(lengths):.2f}", repeats])
return summary_rows, raw_rows
def save_csv(path, rows):
with open(path, "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerows(rows)
def plot_summary(summary_rows):
by_maze = {}
for row in summary_rows[1:]:
maze_name, strategy, avg_time, avg_visited, avg_len, runs = row
by_maze.setdefault(maze_name, []).append((strategy, float(avg_time), float(avg_visited), float(avg_len)))
for maze_name, items in by_maze.items():
items.sort(key=lambda t: t[0])
strategies = [i[0] for i in items]
x = list(range(len(strategies)))
plt.figure(figsize=(8, 4))
plt.bar(x, [i[1] for i in items])
plt.xticks(x, strategies)
plt.ylabel("ms")
plt.title(f"{maze_name} — avg time")
plt.tight_layout()
plt.savefig(OUT_DIR / f"{maze_name}_time.png", dpi=150)
plt.close()
plt.figure(figsize=(8, 4))
plt.bar(x, [i[2] for i in items])
plt.xticks(x, strategies)
plt.ylabel("cells")
plt.title(f"{maze_name} — visited cells")
plt.tight_layout()
plt.savefig(OUT_DIR / f"{maze_name}_visited.png", dpi=150)
plt.close()
plt.figure(figsize=(8, 4))
plt.bar(x, [i[3] for i in items])
plt.xticks(x, strategies)
plt.ylabel("cells")
plt.title(f"{maze_name} — path length")
plt.tight_layout()
plt.savefig(OUT_DIR / f"{maze_name}_length.png", dpi=150)
plt.close()
def main():
OUT_DIR.mkdir(exist_ok=True)
strategies = [
("BFS", BFSStrategy),
("DFS", DFSStrategy),
("A*", AStarStrategy),
("Dijkstra", DijkstraStrategy),
]
mazes = [
("small_10x10", generate_simple_maze(10, 10)),
("medium_50x50", generate_branching_maze(50, 50)),
("large_100x100", generate_branching_maze(100, 100, seed=99, wall_density=0.35)),
("empty_30x30", generate_empty_maze(30, 30)),
("no_path_30x30", generate_no_path_maze(30, 30)),
("weighted_30x30", generate_weighted_maze(30, 30)),
]
summary = [["maze", "strategy", "avg_time_ms", "avg_visited_cells", "avg_path_length", "runs"]]
raw = [["maze", "strategy", "run", "time_ms", "visited_cells", "path_length"]]
for maze_name, maze in mazes:
s_rows, r_rows = bench_one_maze(maze_name, maze, strategies, repeats=5)
summary.extend(s_rows)
raw.extend(r_rows)
save_csv(OUT_DIR / "summary.csv", summary)
save_csv(OUT_DIR / "raw.csv", raw)
plot_summary(summary)
print("Saved to", OUT_DIR.resolve())
if __name__ == "__main__":
main()

View File

@ -0,0 +1,121 @@
maze,strategy,run,time_ms,visited_cells,path_length
small_10x10,BFS,1,0.086300,15,15
small_10x10,BFS,2,0.061100,15,15
small_10x10,BFS,3,0.059300,15,15
small_10x10,BFS,4,0.058400,15,15
small_10x10,BFS,5,0.058500,15,15
small_10x10,DFS,1,0.073400,15,15
small_10x10,DFS,2,0.063500,15,15
small_10x10,DFS,3,0.062500,15,15
small_10x10,DFS,4,0.062700,15,15
small_10x10,DFS,5,0.070900,15,15
small_10x10,A*,1,0.110100,15,15
small_10x10,A*,2,0.089200,15,15
small_10x10,A*,3,0.087800,15,15
small_10x10,A*,4,0.087600,15,15
small_10x10,A*,5,0.087000,15,15
small_10x10,Dijkstra,1,0.290000,15,15
small_10x10,Dijkstra,2,0.083300,15,15
small_10x10,Dijkstra,3,0.091500,15,15
small_10x10,Dijkstra,4,0.081000,15,15
small_10x10,Dijkstra,5,0.080400,15,15
medium_50x50,BFS,1,6.799200,1579,95
medium_50x50,BFS,2,6.960100,1579,95
medium_50x50,BFS,3,6.337000,1579,95
medium_50x50,BFS,4,7.431700,1579,95
medium_50x50,BFS,5,6.517900,1579,95
medium_50x50,DFS,1,6.463000,1277,647
medium_50x50,DFS,2,6.815500,1277,647
medium_50x50,DFS,3,5.816100,1277,647
medium_50x50,DFS,4,6.492400,1277,647
medium_50x50,DFS,5,6.532500,1277,647
medium_50x50,A*,1,6.940500,927,95
medium_50x50,A*,2,7.275400,927,95
medium_50x50,A*,3,7.062500,927,95
medium_50x50,A*,4,7.727600,927,95
medium_50x50,A*,5,7.321000,927,95
medium_50x50,Dijkstra,1,11.483200,1579,95
medium_50x50,Dijkstra,2,11.194200,1579,95
medium_50x50,Dijkstra,3,11.255200,1579,95
medium_50x50,Dijkstra,4,10.512500,1579,95
medium_50x50,Dijkstra,5,10.696400,1579,95
large_100x100,BFS,1,25.623500,5566,195
large_100x100,BFS,2,24.348800,5566,195
large_100x100,BFS,3,25.452600,5566,195
large_100x100,BFS,4,30.516900,5566,195
large_100x100,BFS,5,33.694700,5566,195
large_100x100,DFS,1,19.415200,3543,1531
large_100x100,DFS,2,19.919000,3543,1531
large_100x100,DFS,3,19.104600,3543,1531
large_100x100,DFS,4,20.000600,3543,1531
large_100x100,DFS,5,17.840200,3543,1531
large_100x100,A*,1,7.509300,853,195
large_100x100,A*,2,7.221200,853,195
large_100x100,A*,3,6.486700,853,195
large_100x100,A*,4,6.357600,853,195
large_100x100,A*,5,6.723800,853,195
large_100x100,Dijkstra,1,40.782300,5571,195
large_100x100,Dijkstra,2,41.155000,5571,195
large_100x100,Dijkstra,3,39.456200,5571,195
large_100x100,Dijkstra,4,41.388700,5571,195
large_100x100,Dijkstra,5,40.962500,5571,195
empty_30x30,BFS,1,4.143200,896,55
empty_30x30,BFS,2,3.987000,896,55
empty_30x30,BFS,3,3.777100,896,55
empty_30x30,BFS,4,3.682300,896,55
empty_30x30,BFS,5,3.737900,896,55
empty_30x30,DFS,1,4.024200,842,815
empty_30x30,DFS,2,4.333900,842,815
empty_30x30,DFS,3,5.411000,842,815
empty_30x30,DFS,4,4.677200,842,815
empty_30x30,DFS,5,5.177400,842,815
empty_30x30,A*,1,6.603700,784,55
empty_30x30,A*,2,6.200600,784,55
empty_30x30,A*,3,6.798400,784,55
empty_30x30,A*,4,7.178500,784,55
empty_30x30,A*,5,6.660800,784,55
empty_30x30,Dijkstra,1,6.396000,896,55
empty_30x30,Dijkstra,2,6.275200,896,55
empty_30x30,Dijkstra,3,6.845700,896,55
empty_30x30,Dijkstra,4,6.531200,896,55
empty_30x30,Dijkstra,5,6.783400,896,55
no_path_30x30,BFS,1,2.000100,450,0
no_path_30x30,BFS,2,1.797900,450,0
no_path_30x30,BFS,3,1.796200,450,0
no_path_30x30,BFS,4,1.774100,450,0
no_path_30x30,BFS,5,1.775200,450,0
no_path_30x30,DFS,1,2.090400,450,0
no_path_30x30,DFS,2,2.222600,450,0
no_path_30x30,DFS,3,2.454300,450,0
no_path_30x30,DFS,4,2.476200,450,0
no_path_30x30,DFS,5,2.073700,450,0
no_path_30x30,A*,1,3.651700,450,0
no_path_30x30,A*,2,3.495200,450,0
no_path_30x30,A*,3,3.754200,450,0
no_path_30x30,A*,4,3.286800,450,0
no_path_30x30,A*,5,3.335200,450,0
no_path_30x30,Dijkstra,1,3.050900,450,0
no_path_30x30,Dijkstra,2,3.109900,450,0
no_path_30x30,Dijkstra,3,3.292500,450,0
no_path_30x30,Dijkstra,4,3.418600,450,0
no_path_30x30,Dijkstra,5,3.212100,450,0
weighted_30x30,BFS,1,3.418900,788,55
weighted_30x30,BFS,2,3.368200,788,55
weighted_30x30,BFS,3,3.516400,788,55
weighted_30x30,BFS,4,3.224300,788,55
weighted_30x30,BFS,5,3.131100,788,55
weighted_30x30,DFS,1,3.291200,693,479
weighted_30x30,DFS,2,3.362300,693,479
weighted_30x30,DFS,3,3.523200,693,479
weighted_30x30,DFS,4,3.521400,693,479
weighted_30x30,DFS,5,3.332300,693,479
weighted_30x30,A*,1,1.181000,126,55
weighted_30x30,A*,2,1.080200,126,55
weighted_30x30,A*,3,1.368400,126,55
weighted_30x30,A*,4,1.109800,126,55
weighted_30x30,A*,5,1.079300,126,55
weighted_30x30,Dijkstra,1,6.112700,781,55
weighted_30x30,Dijkstra,2,5.464800,781,55
weighted_30x30,Dijkstra,3,5.794500,781,55
weighted_30x30,Dijkstra,4,6.171700,781,55
weighted_30x30,Dijkstra,5,6.640500,781,55
Internal Server Error - DRE.lab repo

Internal Server Error

Gitea Version: 1.22.0