[2] task 1 and task 2 #160

Open
varavinvv wants to merge 16 commits from varavinvv/2026-rff_mp:varavinvv into develop
20 changed files with 1031 additions and 0 deletions

3
VaravinVV/.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"python-envs.defaultEnvManager": "ms-python.python:system"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -0,0 +1,31 @@
Structure,Mode,Repeat,Insert (sec),Search (sec),Delete (sec)
LinkedList,random,1,0.020151,0.002076,0.000330
LinkedList,random,2,0.019539,0.001570,0.000161
LinkedList,random,3,0.020193,0.001963,0.000151
LinkedList,random,4,0.020033,0.001751,0.000161
LinkedList,random,5,0.019602,0.002023,0.000175
LinkedList,sorted,1,0.020010,0.002227,0.000175
LinkedList,sorted,2,0.019032,0.001596,0.000122
LinkedList,sorted,3,0.019683,0.001889,0.000195
LinkedList,sorted,4,0.019917,0.001636,0.000215
LinkedList,sorted,5,0.019039,0.001624,0.000141
HashTable,random,1,0.003015,0.000273,0.000024
HashTable,random,2,0.002447,0.000214,0.000020
HashTable,random,3,0.002656,0.000226,0.000026
HashTable,random,4,0.002447,0.000205,0.000022
HashTable,random,5,0.002181,0.000381,0.000021
HashTable,sorted,1,0.002358,0.000309,0.000025
HashTable,sorted,2,0.002539,0.000205,0.000019
HashTable,sorted,3,0.002286,0.000234,0.000023
HashTable,sorted,4,0.002566,0.000223,0.000019
HashTable,sorted,5,0.002144,0.000230,0.000022
BST,random,1,0.001556,0.000107,0.000021
BST,random,2,0.001631,0.000116,0.000019
BST,random,3,0.001351,0.000106,0.000016
BST,random,4,0.001378,0.000148,0.000017
BST,random,5,0.001617,0.000121,0.000017
BST,sorted,1,0.066839,0.006176,0.000532
BST,sorted,2,0.064324,0.005361,0.000521
BST,sorted,3,0.065574,0.005315,0.000562
BST,sorted,4,0.063277,0.004858,0.000487
BST,sorted,5,0.058764,0.005325,0.000693
1 Structure Mode Repeat Insert (sec) Search (sec) Delete (sec)
2 LinkedList random 1 0.020151 0.002076 0.000330
3 LinkedList random 2 0.019539 0.001570 0.000161
4 LinkedList random 3 0.020193 0.001963 0.000151
5 LinkedList random 4 0.020033 0.001751 0.000161
6 LinkedList random 5 0.019602 0.002023 0.000175
7 LinkedList sorted 1 0.020010 0.002227 0.000175
8 LinkedList sorted 2 0.019032 0.001596 0.000122
9 LinkedList sorted 3 0.019683 0.001889 0.000195
10 LinkedList sorted 4 0.019917 0.001636 0.000215
11 LinkedList sorted 5 0.019039 0.001624 0.000141
12 HashTable random 1 0.003015 0.000273 0.000024
13 HashTable random 2 0.002447 0.000214 0.000020
14 HashTable random 3 0.002656 0.000226 0.000026
15 HashTable random 4 0.002447 0.000205 0.000022
16 HashTable random 5 0.002181 0.000381 0.000021
17 HashTable sorted 1 0.002358 0.000309 0.000025
18 HashTable sorted 2 0.002539 0.000205 0.000019
19 HashTable sorted 3 0.002286 0.000234 0.000023
20 HashTable sorted 4 0.002566 0.000223 0.000019
21 HashTable sorted 5 0.002144 0.000230 0.000022
22 BST random 1 0.001556 0.000107 0.000021
23 BST random 2 0.001631 0.000116 0.000019
24 BST random 3 0.001351 0.000106 0.000016
25 BST random 4 0.001378 0.000148 0.000017
26 BST random 5 0.001617 0.000121 0.000017
27 BST sorted 1 0.066839 0.006176 0.000532
28 BST sorted 2 0.064324 0.005361 0.000521
29 BST sorted 3 0.065574 0.005315 0.000562
30 BST sorted 4 0.063277 0.004858 0.000487
31 BST sorted 5 0.058764 0.005325 0.000693

View File

@ -0,0 +1,51 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_csv('res.csv')
grouped = df.groupby(['Structure', 'Mode']).agg({
'Insert (sec)': ['mean', 'std'],
'Search (sec)': ['mean', 'std'],
'Delete (sec)': ['mean', 'std']
}).reset_index()
grouped.columns = ['Structure', 'Mode', 'Insert_mean', 'Insert_std',
'Search_mean', 'Search_std', 'Delete_mean', 'Delete_std']
structures = grouped['Structure'].unique()
modes = grouped['Mode'].unique()
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ['Insert', 'Search', 'Delete']
colors = {'random': 'skyblue', 'sorted': 'lightcoral'}
for i, op in enumerate(operations):
ax = axes[i]
mean_col = f'{op}_mean'
std_col = f'{op}_std'
x = np.arange(len(structures))
width = 0.35
for j, mode in enumerate(modes):
data = grouped[grouped['Mode'] == mode]
means = [data[data['Structure'] == s][mean_col].values[0] for s in structures]
stds = [data[data['Structure'] == s][std_col].values[0] for s in structures]
offset = (j - 0.5) * width
bars = ax.bar(x + offset, means, width, yerr=stds, capsize=3,
label=mode.capitalize(), color=colors[mode])
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Time (seconds)')
ax.set_title(f'{op} Time')
ax.legend()
if op == 'Insert':
ax.set_yscale('log')
ax.set_ylabel('Time (seconds) [log scale]')
plt.tight_layout()
plt.savefig('performance_plots.png', dpi=150)
plt.show()
print("Графики сохранены в файл performance_plots.png")

View File

@ -0,0 +1,282 @@
import random
import time
import csv
import sys
sys.setrecursionlimit(20000)
def ll_insert(head, name, phone):
data = {'name': name, 'phone': phone, "next": None}
if head is None:
return data
current = head
while current:
if current['name'] == name:
current['phone'] = phone
return head
if current['next'] is None:
last = current
current = current['next']
last['next'] = data
return head
def ll_find(head, name):
current = head
while current:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
current = head['next']
while current:
if current['name'] == name:
prev['next'] = current['next']
return head
prev = current
current = current['next']
return head
def ll_list_all(head):
data_list = []
current = head
while current:
data_list.append({'name': current['name'], 'phone': current['phone']})
current = current['next']
data_list.sort(key=lambda x: x['name'])
return data_list
def hash_function(name, size):
return hash(name) % size
def ht_insert(buckets, name, phone):
index = hash_function(name, len(buckets))
head = buckets[index]
new_head = ll_insert(head, name, phone)
buckets[index] = new_head
return buckets
def ht_find(buckets, name):
index = hash_function(name, len(buckets))
head = buckets[index]
return ll_find(head, name)
def ht_delete(buckets, name):
index = hash_function(name, len(buckets))
head = buckets[index]
new_head = ll_delete(head, name)
buckets[index] = new_head
return buckets
def ht_list_all(buckets):
all_records = []
for head in buckets:
current = head
while current is not None:
all_records.append((current['name'], current['phone']))
current = current['next']
all_records.sort(key=lambda x: x[0])
return all_records
def create_node(name, phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
def bst_insert(root, name, phone):
if root is None:
return create_node(name, phone)
if name == root['name']:
root['phone'] = phone
elif name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
else:
root['right'] = bst_insert(root['right'], name, phone)
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
elif name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
min_node = find_min(root['right'])
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
result = []
def inorder(node):
if node is None:
return
inorder(node['left'])
result.append((node['name'], node['phone']))
inorder(node['right'])
inorder(root)
return result
def generate_records(n, seed=50): #почти в точности позаимствовано, просто понял что можно уже существующие в отдельный список заносить
random.seed(seed)
records = []
for i in range(1, n + 1):
name = f"User_{i:05d}"
phone = "8" + ''.join(str(random.randint(0, 9)) for _ in range(10))
records.append((name, phone))
return records
def prepare_datasets(base_records):
shuffled = base_records.copy()
random.shuffle(shuffled)
sorted_records = sorted(base_records, key=lambda x: x[0])
return shuffled, sorted_records
def run_experiment(struct_funcs, records, mode_name, repeats=5):
results = []
for rep in range(repeats):
struct = struct_funcs['create']()
start = time.perf_counter()
for name, phone in records:
struct = struct_funcs['insert'](struct, name, phone)
end = time.perf_counter()
insert_time = end - start
existing_names = [name for name, _ in records]
sample_existing = random.sample(existing_names, 100)
nonexistent = [f"None_{i}" for i in range(10)]
search_names = sample_existing + nonexistent
random.shuffle(search_names)
start = time.perf_counter()
for name in search_names:
_ = struct_funcs['find'](struct, name)
end = time.perf_counter()
find_time = end - start
to_delete = random.sample(existing_names, 10)
start = time.perf_counter()
for name in to_delete:
struct = struct_funcs['delete'](struct, name)
end = time.perf_counter()
delete_time = end - start
results.append({
'structure': struct_funcs['name'],
'mode': mode_name,
'repetition': rep + 1,
'insert_time': insert_time,
'find_time': find_time,
'delete_time': delete_time
})
return results
def main():
N = 1000
base_records = generate_records(N)
shuffled, sorted_records = prepare_datasets(base_records)
structures = {
'LinkedList': {
'name': 'LinkedList',
'create': lambda: None,
'insert': ll_insert,
'find': ll_find,
'delete': ll_delete,
'list_all': ll_list_all
},
'HashTable': {
'name': 'HashTable',
'create': lambda: [None] * 10,
'insert': ht_insert,
'find': ht_find,
'delete': ht_delete,
'list_all': ht_list_all
},
'BST': {
'name': 'BST',
'create': lambda: None,
'insert': bst_insert,
'find': bst_find,
'delete': bst_delete,
'list_all': bst_list_all
}
}
all_results = []
repeats = 5
for struct_name, funcs in structures.items():
print(f"Тестирование {struct_name} на случайном порядке...")
res = run_experiment(funcs, shuffled, 'random', repeats)
all_results.extend(res)
print(f"Тестирование {struct_name} на отсортированном порядке...")
res = run_experiment(funcs, sorted_records, 'sorted', repeats)
all_results.extend(res)
with open('res.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Structure', 'Mode', 'Repeat', 'Insert (sec)', 'Search (sec)', 'Delete (sec)'])
for r in all_results:
writer.writerow([
r['structure'],
r['mode'],
r['repetition'],
f"{r['insert_time']:.6f}",
f"{r['find_time']:.6f}",
f"{r['delete_time']:.6f}"
])
if __name__ == '__main__':
main()

View File

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

View File

@ -0,0 +1 @@
S E

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

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

View File

@ -0,0 +1,427 @@
import abc
import heapq
import time
from collections import deque
from dataclasses import dataclass
from typing import List, Optional, Dict, Set, Tuple, Any
import csv
import os
import sys
class Cell:
#тут что такое клетка
def __init__(self, x: int, y: int, is_wall: bool = False,
is_exit: bool = False, is_start: bool = False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_exit = is_exit
self.is_start = is_start
def __eq__(self, other):
return isinstance(other, Cell) and self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
def is_passable(self) -> bool:
return not self.is_wall
def __repr__(self) -> str:
return f"Cell({self.x},{self.y})"
class Maze:
def __init__(self, width: int, height: int): #что содержит лабиринт, начало конец и тд
self.width = width
self.height = height
self.grid: List[List[Cell]] = []
self.start_cell: Optional[Cell] = None
self.exit_cell: Optional[Cell] = None
def set_cell(self, x: int, y: int, cell: Cell) -> None: #ставим клетку куда надо или не ставим если в границы не попала
if not (0 <= x < self.width and 0 <= y < self.height):
raise IndexError("координаты вне границ лабиринта")
self.grid[y][x] = cell
def get_cell(self, x: int, y: int) -> Optional[Cell]: #тут уже из коррдинат клетку вытаскиваем
if 0 <= x < self.width and 0 <= y < self.height:
return self.grid[y][x]
return None
def get_neighbors(self, cell: Cell) -> List[Cell]: #если соседняя клетка проходима - добавляем
neighbors = []
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
nx, ny = cell.x + dx, cell.y + dy
neighbor = self.get_cell(nx, ny)
if neighbor and neighbor.is_passable():
neighbors.append(neighbor)
return neighbors
class MazeBuilder(abc.ABC):
@abc.abstractmethod
def build_from_file(self, filename: str) -> Maze:
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename: str) -> Maze:
lines = []
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.rstrip('\n')
if line: #игнорируем пустые строки
lines.append(line)
if not lines:
raise ValueError("Файл пуст")
height = len(lines)
width = max(len(line) for line in lines)
maze = Maze(width, height)
#инициализируем сетку пустыми клетками,по умолчанию стенами
maze.grid = [[Cell(x, y, is_wall=True) for x in range(width)] for y in range(height)]
start_cell = None
exit_cell = None
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if x >= width:
continue
if ch == '#':
continue
elif ch == ' ':
cell = Cell(x, y, is_wall=False)
elif ch == 'S':
cell = Cell(x, y, is_wall=False, is_start=True)
start_cell = cell
elif ch == 'E':
cell = Cell(x, y, is_wall=False, is_exit=True)
exit_cell = cell
else:
#любой другой символ считаем проходом
cell = Cell(x, y, is_wall=False)
maze.set_cell(x, y, cell)
if start_cell is None:
raise ValueError("отсутствует стартовая клетка (S)") #invalid check
if exit_cell is None:
raise ValueError("отсутствует выход (E)")
maze.start_cell = start_cell
maze.exit_cell = exit_cell
return maze
class PathFindingStrategy(abc.ABC):
@abc.abstractmethod
def find_path(self, maze: Maze, start: Cell, exit_: Cell) -> Tuple[List[Cell], int]:
pass
#дальше скорее математика, методы вроде ещё в том семаке разбирали
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze: Maze, start: Cell, exit_: Cell) -> Tuple[List[Cell], int]:
if start is exit_:
return [start], 1
queue = deque([start]) #используйте deque 👍
visited: Set[Cell] = {start}
parent: Dict[Cell, Optional[Cell]] = {start: None}
while queue:
current = queue.popleft()
if current is exit_:
#восстановление пути
path = []
cur = current
while cur is not None:
path.append(cur)
cur = parent[cur]
path.reverse()
return path, len(visited)
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
return [], len(visited)
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze: Maze, start: Cell, exit_: Cell) -> Tuple[List[Cell], int]:
if start is exit_:
return [start], 1
stack = [start]
visited: Set[Cell] = {start}
parent: Dict[Cell, Optional[Cell]] = {start: None}
while stack:
current = stack.pop()
if current is exit_:
path = []
cur = current
while cur is not None:
path.append(cur)
cur = parent[cur]
path.reverse()
return path, len(visited)
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
stack.append(neighbor)
return [], len(visited)
class AStarStrategy(PathFindingStrategy):
@staticmethod
def _heuristic(cell: Cell, target: Cell) -> int:
return abs(cell.x - target.x) + abs(cell.y - target.y) #самая простая эвристика
def find_path(self, maze: Maze, start: Cell, exit_: Cell) -> Tuple[List[Cell], int]:
if start is exit_:
return [start], 1
counter = 0
open_set = [(0, counter, start)]
g_score: Dict[Cell, int] = {start: 0}
f_score: Dict[Cell, int] = {start: self._heuristic(start, exit_)}
parent: Dict[Cell, Optional[Cell]] = {start: None}
closed_set: Set[Cell] = set()
visited_count = 0
while open_set:
_, _, current = heapq.heappop(open_set)
if current in closed_set:
continue
closed_set.add(current)
visited_count = len(closed_set)
if current is exit_:
path = []
cur = current
while cur is not None:
path.append(cur)
cur = parent[cur]
path.reverse()
return path, visited_count
for neighbor in maze.get_neighbors(current):
if neighbor in closed_set:
continue
tentative_g = g_score[current] + 1
if neighbor not in g_score or tentative_g < g_score[neighbor]:
parent[neighbor] = current
g_score[neighbor] = tentative_g
f = tentative_g + self._heuristic(neighbor, exit_)
f_score[neighbor] = f
counter += 1
heapq.heappush(open_set, (f, counter, neighbor))
return [], visited_count
@dataclass
class SearchStats:
time_ms: float #время выполнения в мс
visited_cells: int #количество посещённых клеток
path_length: int #длина найденного пути (0 если пути нет)
path_found: bool #найден ли путь
class Observer(abc.ABC): #я забыл что я там писать хотел после наблюдателя удачи мне завтра разобрать
@abc.abstractmethod #а я (гугл + хабр) разобрал снова балбесина!!!
def update(self, event_type: str, data: Any = None) -> None:
pass
class Subject:
def __init__(self):
self._observers: List[Observer] = []
def attach(self, observer: Observer) -> None:
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer: Observer) -> None:
if observer in self._observers:
self._observers.remove(observer)
def notify(self, event_type: str, data: Any = None) -> None:
for obs in self._observers:
obs.update(event_type, data)
class MazeSolver(Subject):
def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None):
super().__init__()
self.maze = maze
self._strategy = strategy
def set_strategy(self, strategy: PathFindingStrategy) -> None:
self._strategy = strategy
def solve(self) -> Optional[SearchStats]:
if self._strategy is None:
return None
start_time = time.perf_counter()
path, visited = self._strategy.find_path(self.maze, self.maze.start_cell, self.maze.exit_cell)
end_time = time.perf_counter()
time_ms = (end_time - start_time) * 1000.0
return SearchStats(time_ms, visited, len(path), len(path) > 0)
class Benchmark:
def __init__(self, maze_files: List[str], runs_per_strategy: int = 5):
self.maze_files = maze_files
self.runs = runs_per_strategy
self.strategies = {
"BFS": BFSStrategy(),
"DFS": DFSStrategy(),
"AStar": AStarStrategy()
}
self.builder = TextFileMazeBuilder()
self.results = []
def run(self, output_csv: str):
for maze_file in self.maze_files:
if not os.path.exists(maze_file):
print(f"файл {maze_file} не найден")
continue
try:
maze = self.builder.build_from_file(maze_file)
except Exception as e:
print(f"ошибка загрузки {maze_file}: {e}")
continue
print(f"обработка лабиринта: {maze_file} (размер {maze.width}x{maze.height})")
for strat_name, strategy in self.strategies.items():
solver = MazeSolver(maze, strategy)
times = []
visited_list = []
path_lengths = []
path_found = False
for run_idx in range(self.runs):
stats = solver.solve()
if stats is None:
continue
times.append(stats.time_ms)
visited_list.append(stats.visited_cells)
path_lengths.append(stats.path_length)
path_found = stats.path_found
if times:
avg_time = sum(times) / len(times)
avg_visited = sum(visited_list) / len(visited_list)
avg_length = sum(path_lengths) / len(path_lengths)
else:
avg_time = avg_visited = avg_length = 0.0
self.results.append({
"лабиринт": os.path.basename(maze_file),
"стратегия": strat_name,
"время_мс": round(avg_time, 3),
"посещено_клеток": round(avg_visited, 1),
"длина_пути": round(avg_length, 1),
"путь_найден": path_found
})
print(f" {strat_name}: {avg_time:.3f} мс, посещено {avg_visited:.1f}, длина {avg_length:.1f}")
with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ["лабиринт", "стратегия", "время_мс", "посещено_клеток", "длина_пути", "путь_найден"]
writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=',')
writer.writeheader()
for row in self.results:
writer.writerow(row)
print(f"\nрезультаты сохранены в {output_csv}")
def interactive_mode(maze_file: str):
builder = TextFileMazeBuilder()
try:
maze = builder.build_from_file(maze_file)
except Exception as e:
print(f"ошибка загрузки лабиринта: {e}")
return
strategies = {
"1": ("BFS", BFSStrategy()),
"2": ("DFS", DFSStrategy()),
"3": ("A*", AStarStrategy())
}
print("\nвыберите алгоритм поиска:")
print("1. BFS")
print("2. DFS")
print("3. A*")
choice = input("введите (1/2/3): ").strip()
if choice not in strategies:
print("неверный выбор, по умолчанию используется BFS.")
strat_name, strategy = strategies["1"]
else:
strat_name, strategy = strategies[choice]
solver = MazeSolver(maze, strategy)
stats = solver.solve()
if stats is None:
print("ошибка с решением")
return
path, _ = strategy.find_path(maze, maze.start_cell, maze.exit_cell)
path_set = set(path)
for y in range(maze.height):
row = []
for x in range(maze.width):
cell = maze.get_cell(x, y)
if cell is maze.start_cell:
row.append('S')
elif cell is maze.exit_cell:
row.append('E')
elif cell in path_set:
row.append('*')
elif cell and cell.is_wall:
row.append('#')
else:
row.append(' ')
print(''.join(row))
print(f"\nстатистика ({strat_name}):")
print(f"время выполнения: {stats.time_ms:.3f} мс")
print(f"посещено клеток: {stats.visited_cells}")
print(f"длина пути: {stats.path_length}")
print(f"путь найден: {'да' if stats.path_found else 'нет'}")
def main():
if len(sys.argv) < 2:
print("использование:")
print("режим визуализации: python main.py <файл_лабиринта>")
print("режим замера: python main.py --benchmark <список_лабиринтов> --runs <кол_во_итераций> --output <названиеаблицы>.csv")
return
if sys.argv[1] == "--benchmark":
args = sys.argv[2:]
maze_files = []
runs = 5
output = "benchmark_results.csv"
i = 0
while i < len(args):
if args[i] == "--runs" and i+1 < len(args):
runs = int(args[i+1])
i += 2
elif args[i] == "--output" and i+1 < len(args):
output = args[i+1]
i += 2
else:
maze_files.append(args[i])
i += 1
if not maze_files:
print("Ошибка: не указаны файлы лабиринтов.")
return
benchmark = Benchmark(maze_files, runs_per_strategy=runs)
benchmark.run(output)
else:
interactive_mode(sys.argv[1])
if __name__ == "__main__":
main()

View File

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

View File

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

View File

@ -0,0 +1,16 @@
лабиринт,стратегия,время_мс,посещено_клеток,длина_пути,путь_найден
easy.txt,BFS,0.046,32.0,17.0,True
easy.txt,DFS,0.026,28.0,17.0,True
easy.txt,AStar,0.037,21.0,17.0,True
medium.txt,BFS,1.141,1249.0,101.0,True
medium.txt,DFS,0.547,633.0,101.0,True
medium.txt,AStar,1.26,721.0,101.0,True
hard.txt,BFS,4.703,4997.0,209.0,True
hard.txt,DFS,3.61,4026.0,209.0,True
hard.txt,AStar,6.119,3377.0,209.0,True
noexit.txt,BFS,0.002,1.0,0.0,False
noexit.txt,DFS,0.002,1.0,0.0,False
noexit.txt,AStar,0.005,1.0,0.0,False
empty.txt,BFS,0.003,3.0,3.0,True
empty.txt,DFS,0.003,3.0,3.0,True
empty.txt,AStar,0.004,3.0,3.0,True
1 лабиринт стратегия время_мс посещено_клеток длина_пути путь_найден
2 easy.txt BFS 0.046 32.0 17.0 True
3 easy.txt DFS 0.026 28.0 17.0 True
4 easy.txt AStar 0.037 21.0 17.0 True
5 medium.txt BFS 1.141 1249.0 101.0 True
6 medium.txt DFS 0.547 633.0 101.0 True
7 medium.txt AStar 1.26 721.0 101.0 True
8 hard.txt BFS 4.703 4997.0 209.0 True
9 hard.txt DFS 3.61 4026.0 209.0 True
10 hard.txt AStar 6.119 3377.0 209.0 True
11 noexit.txt BFS 0.002 1.0 0.0 False
12 noexit.txt DFS 0.002 1.0 0.0 False
13 noexit.txt AStar 0.005 1.0 0.0 False
14 empty.txt BFS 0.003 3.0 3.0 True
15 empty.txt DFS 0.003 3.0 3.0 True
16 empty.txt AStar 0.004 3.0 3.0 True

View File

@ -0,0 +1,53 @@
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import sys
import os
def plot_results(csv_file: str, output_dir: str = "plots"):
if not os.path.exists(csv_file):
print(f"Файл {csv_file} не найден.")
return
df = pd.read_csv(csv_file, encoding='utf-8')
required = ["лабиринт", "стратегия", "время_мс", "посещено_клеток", "длина_пути"]
for col in required:
if col not in df.columns:
print(f"В CSV отсутствует колонка {col}")
return
os.makedirs(output_dir, exist_ok=True)
sns.set_style("whitegrid")
for maze_name in df["лабиринт"].unique():
maze_df = df[df["лабиринт"] == maze_name]
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle(f"Сравнение стратегий для лабиринта {maze_name}", fontsize=14)
ax = axes[0]
sns.barplot(data=maze_df, x="стратегия", y="время_мс", ax=ax, palette="viridis")
ax.set_title("Время выполнения (мс)")
ax.set_ylabel("мс")
ax = axes[1]
sns.barplot(data=maze_df, x="стратегия", y="посещено_клеток", ax=ax, palette="plasma")
ax.set_title("Количество посещённых клеток")
ax.set_ylabel("клетки")
ax = axes[2]
sns.barplot(data=maze_df, x="стратегия", y="длина_пути", ax=ax, palette="coolwarm")
ax.set_title("Длина найденного пути")
ax.set_ylabel("шаги")
plt.tight_layout()
plt.savefig(os.path.join(output_dir, f"results_{maze_name}.png"), dpi=150)
plt.close()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Использование: python tables.py <csv_файл> <папка_для_сохранения>")
print("Пример: python tables.py res.csv plots")
sys.exit(1)
csv_file = sys.argv[1]
out_dir = sys.argv[2] if len(sys.argv) > 2 else "plots"
plot_results(csv_file, out_dir)

Binary file not shown.

Binary file not shown.