Compare commits

..

44 Commits

Author SHA1 Message Date
82e988c965 Merge pull request '[0] initial commit' (#170) from Smirnovvs/2026-rff_mp:Smirnovvs into develop
Reviewed-on: UNN/2026-rff_mp#170
2026-04-27 18:05:51 +00:00
58daf860ed Merge pull request '[0] initial commit' (#167) from kalinovskiymi/2026-rff_mp:kalinovskiymi into develop
Reviewed-on: UNN/2026-rff_mp#167
2026-04-27 18:05:12 +00:00
566d89fda2 Merge pull request '[0] initial commit' (#166) from dyachenkoas/2026-rff_mp:dyachenkoas into develop
Reviewed-on: UNN/2026-rff_mp#166
2026-04-25 09:27:44 +00:00
c7229154ca Merge pull request '[0] initial commit' (#158) from MalkinMV/2026-rff_mp:MalkinMV into develop
Reviewed-on: UNN/2026-rff_mp#158
2026-04-25 09:25:55 +00:00
6e4ae1835b Merge pull request '[1] initial commit' (#171) from osipovamd/2026-rff_mp:osipovamd into develop
Reviewed-on: UNN/2026-rff_mp#171
2026-04-05 10:54:06 +00:00
MariiaOs
25341dc814 [1] initial commit 2026-04-04 17:13:00 +03:00
fe9ce65eb2 [0] initial commit 2026-04-04 14:46:15 +03:00
405d1e583b Merge pull request '[0] initial commit' (#162) from varnakovaa/2026-rff_mp:VarnakovAA into develop
Reviewed-on: UNN/2026-rff_mp#162
2026-04-04 09:12:00 +00:00
74807f5514 Обновить README.md 2026-04-04 07:11:52 +00:00
3a251f06c7 Добавить kalinovskiymi/428 2026-03-27 20:48:17 +00:00
e4c7e2d97a [0] initial commit 2026-03-23 21:32:01 +03:00
MalkinMV
7e84caffc4 [0] initial commit 2026-03-21 10:29:10 +03:00
84e5d1e763 Добавить dyachenkoas/428 2026-03-21 03:26:23 +00:00
f5b0fec46f Merge pull request '[1] FINISH 1-st-exercise' (#148) from IvanBud123/2026-rff_mp:1-st-exercise into develop
Reviewed-on: UNN/2026-rff_mp#148
2026-03-15 06:42:22 +00:00
e4423a3be8 Merge pull request '[0] initial commit' (#154) from BoriskovaDV/2026-rff_mp:BoriskovaDV into develop
Reviewed-on: UNN/2026-rff_mp#154
2026-03-15 06:41:55 +00:00
eeb28d8b6a [0] init 2026-03-15 05:18:35 +00:00
b3e4b6149e docs 2026-03-15 02:46:48 +00:00
c98c8a472b Merge pull request '[0] initial commit' (#152) from victorovaas/2026-rff_mp:victorovaas into develop
Reviewed-on: UNN/2026-rff_mp#152
2026-03-14 09:27:51 +00:00
Git Version Control
666e6ecd41 [1] ... README.md 2026-03-12 04:02:58 +00:00
062d3b983e Merge pull request '[0] initial commit' (#151) from KislyuninED/2026-rff_mp:KislyuninED into develop
Reviewed-on: UNN/2026-rff_mp#151
2026-03-10 08:17:33 +00:00
4cef8060a3 [15] FINISH for 1-st exersize 2026-03-04 01:40:38 +03:00
50798eb572 Merge branch 'develop' into 1-st-exercise 2026-03-04 01:37:44 +03:00
7067bfa12a [14] FINISH for 1-st exersize 2026-03-04 01:14:58 +03:00
64f43373df [13] FINISH for 1-st exersize 2026-03-04 01:02:42 +03:00
da65d02bd7 [12] BIG UPDATE 2026-03-04 01:02:40 +03:00
0441c5076a [11] A minor update that fixes errors and typos 2026-03-04 01:02:37 +03:00
28de33a83d [10] A minor update that fixes errors and typos 2026-03-04 01:02:32 +03:00
49e91066d4 [9] finish with adding BST(Binar Search Tree) 2026-03-04 01:02:29 +03:00
bb28c3dd2f [8] start adding binar search func(create_node and bst_insert) 2026-03-04 01:02:26 +03:00
1ebec4223a [7] add hash func and tests of hash func 2026-03-04 01:02:23 +03:00
e868e94fcd [6] add hash func find, insert and hash_func 2026-03-04 01:02:17 +03:00
0e84534109 [5] modyfi LinkedListBook for 2-nd exersize 2026-03-04 01:02:11 +03:00
8c642fea20 [4] start coding hash-table 2026-03-04 00:56:59 +03:00
7dbd3075b6 [3] adding ll_list_all 2026-03-04 00:56:59 +03:00
050462d011 [2] adding ll_find and ll_delete 2026-03-04 00:56:59 +03:00
ba244f24bb [1] implemented the ll_insert function for an first exercise 2026-03-04 00:56:59 +03:00
39fe26b3f1 [0] initial commit 2026-03-04 00:56:59 +03:00
5de2cca73e Обновить README.md 2026-03-04 00:56:59 +03:00
8c17e92dd8 Удалить testfile.txt 2026-03-04 00:56:59 +03:00
b1cb2491d8 Обновить README.md 2026-03-04 00:56:59 +03:00
a5ee4d9ae5 Обновить README.md 2026-03-04 00:56:59 +03:00
fb8f0c47f5 Обновить README.md 2026-03-04 00:56:59 +03:00
099ec9f5e3 Обновить README.md 2026-03-04 00:56:59 +03:00
ff1063d866 [0] initial commit 2026-02-28 17:19:44 +03:00
20 changed files with 784 additions and 487 deletions

0
BoriskovaDV/428.md Normal file
View File

View File

@ -0,0 +1,457 @@
head = None
#node1 = {'name' : 'Ivan', 'phone' : '123-456', 'next' : None}
#head = node1
#node2 = {'name' : 'Dima', 'phone' : '789-123', 'next' : None}
#node1['next'] = node2
def ll_insert(head, name, phone):
curent = head
while curent is not None:
if curent['name'] == name:
curent['phone'] = phone
return head
curent = curent['next']
n_node = {'name' : name, 'phone' : phone, 'next' : None}
if head is None:
return n_node
curent = head
while curent['next'] is not None:
curent = curent['next']
curent['next'] = n_node
return head
print("====== TESTING ll_insert FUNC ========")
head = ll_insert(head,'Ivan','123-456')
print(head)
head = ll_insert(head, 'Boris', '123-456')
print(head)
head = ll_insert(head, 'Ivan', '321-654')
print(head)
head = ll_insert(head, 'Dima', '345-678')
print(head)
head = ll_insert(head, 'Boris', '111-222')
print(head)
head = ll_insert(head, 'Methody', '221-112')
head = ll_insert(head, 'Kiril', '112-221')
print(f"======= END TEST =======\n\n\n")
def ll_find(head, name):
curent = head
while curent is not None:
if curent['name'] == name:
return curent['phone']
curent = curent['next']
return None
print("====== TESTING ll_find FUNC ======")
print("Ivan`s phone: "+ ll_find(head, 'Ivan'))
print("Dima`s phone: "+ ll_find(head, 'Dima'))
print("Boris phone: "+ ll_find(head, 'Boris'))
print(f"====== END TEST ======\n\n\n")
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
curent = head['next']
while curent is not None:
if curent['name'] == name:
prev['next'] = curent['next']
return head
prev = curent
curent = curent['next']
return head
print("====== TEST ll_delete FUNC ======")
print("Del of Dima:", ll_delete(head, 'Dima'))
print("====== END TEST ======")
def ll_list_all(head):
records = []
curent = head
while curent is not None:
records.append((curent['name'],curent['phone']))
curent = curent['next']
records.sort(key=lambda pair: pair[0])
return records
print(f"\n\n\n\n")
print("====== TESTING ll_list_all FUNC ======")
print(ll_list_all(head))
print("====== END ======")
#============================== HASH FUNCTIONS =========================
SIZE = 5
buckets = [None] * SIZE
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
print(f"\n\n\n ====== TEST INSERT HASH ======")
print(buckets)
ht_insert(buckets, "Ivan", "123-456")
print(buckets)
ht_insert(buckets, "Dima", "789-123")
print(buckets)
ht_insert(buckets, "Boris", "456-789")
print(buckets)
print("====== END TEST ======\n\n\n")
def ht_find(buckets, name):
index = hash_function(name, len(buckets))
head = buckets[index]
return ll_find(head, name)
print("====== TEST FIND HASH FUN ======")
print("find by name Ivan: ",ht_find(buckets, "Ivan"))
print("find by name Dima: ",ht_find(buckets, "Dima"))
print("find by name Boris: ", ht_find(buckets, "Boris"))
print("====== END TEST ======\n\n\n")
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
print("====== TEST FUNC LIST ALL ======")
print(ht_list_all(buckets))
print("====== END TEST ======\n\n\n")
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
print("====== GLOBAL TEST FOR HASH BASED FUN ======")
buckets = [None] * 10
ht_insert(buckets, "Ivan", "123-456")
print(buckets)
ht_insert(buckets, "Boris", "789-012")
print(buckets)
ht_insert(buckets, "Anna", "345-678")
print(buckets)
ht_insert(buckets, "Ivan", "111-222") # update
print(buckets)
print("Find Ivan`s phone: ",ht_find(buckets, "Ivan")) # 111-222
print("Find Petr`s phone: ",ht_find(buckets, "Petr")) # None
# Удаляем
print("delite Boris from buckets")
ht_delete(buckets, "Boris")
print("search Boris = ",ht_find(buckets, "Boris")) # None
# Все записи
print("list all records: ",ht_list_all(buckets))
print("====== END GLOBAL TEST ======\n\n\n")
# ======================== TREE FUNC ====================
def create_node(name,phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
print("====== START TREE FUNC CHAPTER ======\n\n")
print("====== TEST CREATE NODE FUNC ======")
root = create_node('Ivan', '123-456')
print("Create Ivan node: ",root)
print("====== END TEST ====== \n\n\n")
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
print("====== TEST INSERT FUNC ======")
root = bst_insert(root, 'Dima', '456-789')
print("add Dima: ", root)
root = bst_insert(root, 'Boris', '789-123')
print("add Boris: ", root)
root = bst_insert(root, 'Eva', '321-123')
print("add Eva: ", root)
print("====== END TEST =======\n\n\n")
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)
print("====== START FIND TEST ======")
print("search by Ivan`s phone: ", bst_find(root, 'Ivan'))
print("search by Eva`s phone: ", bst_find(root,'Eva'))
print("====== END TEST ====== \n\n\n")
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
print("====== GLOBAL TEST TREES ======")
root = None
root = bst_insert(root, "Ivan", "123-456")
print("add Ivan: ", root)
root = bst_insert(root, "Boris", "789-012")
print("add Boris: ", root)
root = bst_insert(root, "Anna", "345-678")
print("add Anna: ", root)
root = bst_insert(root, "Ivan", "111-222") # обновление
print("update Ivan: ", root)
print("Find Ivan`s phone: ",bst_find(root, "Ivan")) # 111-222
print("Find Peter`s phone: ",bst_find(root, "Petr")) # None
root = bst_delete(root, "Boris")
print("Del Boris")
print("Find Boris: ",bst_find(root, "Boris")) # None
print("Find ALL: ",bst_list_all(root)) # [('Anna','345-678'), ('Ivan','111-222')]
print("====== END TEST ======")
# ======================== EXPEREMENT CHAPTER ========================
import random
import time
import csv
import sys
sys.setrecursionlimit(20000)
def generate_records(n, seed=42):
random.seed(seed)
records = []
for i in range(1, n+1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
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']()
# enter all records
start = time.perf_counter()
for name, phone in records:
struct = struct_funcs['insert'](struct, name, phone)
end = time.perf_counter()
insert_time = end - start
# search for 110 records (100 real + 10 None)
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
# delete 10 random records
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"Testing {struct_name} on random order...")
res = run_experiment(funcs, shuffled, 'random', repeats)
all_results.extend(res)
print(f"Testing {struct_name} in sorted order...")
res = run_experiment(funcs, sorted_records, 'sorted', repeats)
all_results.extend(res)
with open('experiment_results.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}"
])
print("The experiment is complete. The results are saved in experiment_results.csv.")
if __name__ == '__main__':
main()

View File

@ -0,0 +1,31 @@
Structure,Mode,Repeat,Insert (sec),Search (sec),Delete (sec)
LinkedList,random,1,0.140358,0.007040,0.000844
LinkedList,random,2,0.138009,0.009197,0.000413
LinkedList,random,3,0.114717,0.009266,0.000744
LinkedList,random,4,0.117224,0.006914,0.000531
LinkedList,random,5,0.136302,0.010432,0.000582
LinkedList,sorted,1,0.106921,0.007845,0.000566
LinkedList,sorted,2,0.116404,0.015005,0.004900
LinkedList,sorted,3,0.125122,0.006956,0.000708
LinkedList,sorted,4,0.122401,0.004220,0.000474
LinkedList,sorted,5,0.111422,0.008343,0.000551
HashTable,random,1,0.025442,0.004652,0.000078
HashTable,random,2,0.035477,0.000985,0.000091
HashTable,random,3,0.015387,0.001249,0.000298
HashTable,random,4,0.014196,0.001167,0.000096
HashTable,random,5,0.013819,0.000910,0.000094
HashTable,sorted,1,0.013713,0.000897,0.000060
HashTable,sorted,2,0.016816,0.001013,0.000116
HashTable,sorted,3,0.018408,0.001019,0.000084
HashTable,sorted,4,0.014490,0.000886,0.000093
HashTable,sorted,5,0.012493,0.000867,0.000075
BST,random,1,0.006755,0.000468,0.000065
BST,random,2,0.006454,0.000380,0.000052
BST,random,3,0.003348,0.000266,0.000033
BST,random,4,0.004785,0.000379,0.000053
BST,random,5,0.005253,0.000438,0.000083
BST,sorted,1,0.331066,0.028260,0.002915
BST,sorted,2,0.342009,0.025769,0.003155
BST,sorted,3,0.282425,0.031293,0.002984
BST,sorted,4,0.313816,0.022712,0.002957
BST,sorted,5,0.287008,0.032645,0.002415
1 Structure Mode Repeat Insert (sec) Search (sec) Delete (sec)
2 LinkedList random 1 0.140358 0.007040 0.000844
3 LinkedList random 2 0.138009 0.009197 0.000413
4 LinkedList random 3 0.114717 0.009266 0.000744
5 LinkedList random 4 0.117224 0.006914 0.000531
6 LinkedList random 5 0.136302 0.010432 0.000582
7 LinkedList sorted 1 0.106921 0.007845 0.000566
8 LinkedList sorted 2 0.116404 0.015005 0.004900
9 LinkedList sorted 3 0.125122 0.006956 0.000708
10 LinkedList sorted 4 0.122401 0.004220 0.000474
11 LinkedList sorted 5 0.111422 0.008343 0.000551
12 HashTable random 1 0.025442 0.004652 0.000078
13 HashTable random 2 0.035477 0.000985 0.000091
14 HashTable random 3 0.015387 0.001249 0.000298
15 HashTable random 4 0.014196 0.001167 0.000096
16 HashTable random 5 0.013819 0.000910 0.000094
17 HashTable sorted 1 0.013713 0.000897 0.000060
18 HashTable sorted 2 0.016816 0.001013 0.000116
19 HashTable sorted 3 0.018408 0.001019 0.000084
20 HashTable sorted 4 0.014490 0.000886 0.000093
21 HashTable sorted 5 0.012493 0.000867 0.000075
22 BST random 1 0.006755 0.000468 0.000065
23 BST random 2 0.006454 0.000380 0.000052
24 BST random 3 0.003348 0.000266 0.000033
25 BST random 4 0.004785 0.000379 0.000053
26 BST random 5 0.005253 0.000438 0.000083
27 BST sorted 1 0.331066 0.028260 0.002915
28 BST sorted 2 0.342009 0.025769 0.003155
29 BST sorted 3 0.282425 0.031293 0.002984
30 BST sorted 4 0.313816 0.022712 0.002957
31 BST sorted 5 0.287008 0.032645 0.002415

View File

@ -0,0 +1,44 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# Загрузка данных
df = pd.read_csv('experiment_results.csv')
# Усреднение по повторам
mean_times = df.groupby(['Structure', 'Mode'])[['Insert (sec)', 'Search (sec)', 'Delete (sec)']].mean().reset_index()
# Подготовка данных для графиков
structures = mean_times['Structure'].unique()
modes = mean_times['Mode'].unique()
# Создание трех графиков (вставка, поиск, удаление)
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ['Insert (sec)', 'Search (sec)', 'Delete (sec)']
titles = ['Вставка', 'Поиск', 'Удаление']
for ax, op, title in zip(axes, operations, titles):
# Для каждой структуры строим две колонки (random, sorted)
x = np.arange(len(structures))
width = 0.35
random_vals = []
sorted_vals = []
for s in structures:
random_row = mean_times[(mean_times['Structure']==s) & (mean_times['Mode']=='random')]
sorted_row = mean_times[(mean_times['Structure']==s) & (mean_times['Mode']=='sorted')]
random_vals.append(random_row[op].values[0] if not random_row.empty else 0)
sorted_vals.append(sorted_row[op].values[0] if not sorted_row.empty else 0)
ax.bar(x - width/2, random_vals, width, label='Случайный')
ax.bar(x + width/2, sorted_vals, width, label='Отсортированный')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Время (сек)')
ax.set_title(title)
ax.legend()
plt.tight_layout()
plt.savefig('../../performance_comparison.png', dpi=150)
plt.show()

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,60 @@
# Отчёт по лабораторной работе "Структуры данных"
## 1. Введение
В рамках работы были реализованы три структуры данных для хранения телефонного справочника: связный список, хеш-таблица и двоичное дерево поиска. Проведено экспериментальное сравнение производительности операций вставки, поиска и удаления на наборе из **10000 записей**. Для каждой структуры тестирование выполнялось на двух вариантах входных данных: случайный порядок и отсортированный по имени. Каждый эксперимент повторялся 5 раз, результаты усреднены.
## 2. Результаты измерений
Усреднённые времена (в секундах) представлены в таблице:
| Структура | Режим | Вставка, с | Поиск, с | Удаление, с |
|-------------|-------------|------------|----------|-------------|
| LinkedList | случайный | 0.1143 | 0.0078 | 0.00065 |
| LinkedList | сортир. | 0.1124 | 0.0068 | 0.00065 |
| HashTable | случайный | 0.0131 | 0.00109 | 0.000085 |
| HashTable | сортир. | 0.0156 | 0.00110 | 0.00014 |
| BST | случайный | 0.00532 | 0.000365 | 0.000053 |
| BST | сортир. | 0.303 | 0.0230 | 0.00268 |
Графическое представление результатов приведено на рисунке ниже.
![Сравнение производительности](performance_comparison.png)
## 3. Анализ результатов
### 3.1. Влияние порядка данных на BST
При вставке элементов в отсортированном порядке двоичное дерево поиска вырождается в линейный список все новые узлы добавляются только в правое поддерево. Высота дерева становится равной количеству элементов, и сложность всех операций возрастает до **O(n)**. Эксперимент подтверждает это:
- Вставка в BST на отсортированных данных заняла **0.303 с**, что в **57 раз** больше, чем на случайных (0.00532 с).
- Время вставки на отсортированных данных даже превышает показатели связного списка (0.112 с), что объясняется дополнительными накладными расходами на рекурсивные вызовы.
- Поиск и удаление также замедлились примерно в 60 раз по сравнению со случайным режимом.
### 3.2. Устойчивость хеш-таблицы к порядку
Хеш-таблица использует хеш-функцию, которая равномерно распределяет ключи по корзинам независимо от порядка поступления. Поэтому производительность операций практически не зависит от того, в каком порядке приходят данные:
- В случайном и отсортированном режимах времена вставки (0.0131 и 0.0156 с) и поиска (около 0.0011 с) близки.
- Небольшие колебания могут быть вызваны случайным распределением коллизий.
- Это соответствует ожидаемой средней сложности **O(1)**.
### 3.3. Медлительность связного списка при поиске
Связный список не обеспечивает прямого доступа к элементам для поиска необходимо просматривать узлы последовательно, что даёт сложность **O(n)**. В эксперименте:
- Время поиска в списке (~0.007 с) на порядок больше, чем в хеш-таблице (0.0011 с) и BST на случайных данных (0.00037 с).
- При увеличении объёма данных эта разница будет только расти.
- Вставка в список также относительно медленна (0.11 с), так как требует прохода до конца (хотя обновление существующего имени выполняется быстрее, но в тесте все имена уникальны, поэтому каждая вставка проходит весь список).
### 3.4. Сравнение удаления
- **Связный список**: удаление требует сначала найти элемент (O(n)), затем переставить ссылки (O(1)). Время удаления (0.00065 с) близко ко времени поиска, что логично.
- **Хеш-таблица**: удаление выполняется за O(1) в среднем сначала определяется корзина, затем из короткого списка удаляется элемент. Время удаления (0.0000850.00014 с) значительно меньше, чем в списке.
- **BST**: на случайных данных удаление очень быстрое (0.000053 с) благодаря логарифмической высоте. На отсортированных данных время возрастает до 0.00268 с (в 50 раз), что отражает деградацию до O(n).
## 4. Выводы и рекомендации по выбору структуры
На основе полученных результатов можно сформулировать следующие рекомендации:
- **Хеш-таблица** оптимальный выбор, если требуется максимальная скорость поиска, вставки и удаления, а порядок хранения не важен. Примеры: реализация словарей, кэшей, индексов по ключу. В эксперименте хеш-таблица показала стабильно высокую производительность во всех режимах.
- **Двоичное дерево поиска** следует применять, когда необходимо получать данные в отсортированном порядке (например, вывод телефонного справочника по алфавиту). Однако важно учитывать, что при поступлении отсортированных данных дерево вырождается, и производительность резко падает. В таких случаях лучше использовать сбалансированные деревья (AVL, красно-чёрные). В эксперименте BST на случайных данных показал отличные результаты, близкие к хеш-таблице, а на отсортированных стал самым медленным.
- **Связный список** практически непригоден для больших объёмов данных из-за линейной сложности основных операций. Может использоваться лишь для очень маленьких коллекций, при частых вставках в начало списка (здесь не рассматривалось) или в учебных целях.
Таким образом, для реальных задач чаще всего выбирают хеш-таблицы или сбалансированные деревья в зависимости от требований к упорядоченности данных.
I use arch BTW

View File

@ -1,88 +0,0 @@
import random
import time
import csv
from phonebook import ll_insert, ll_find, ll_delete, ll_list_all
from phonebook import ht_create, ht_insert, ht_find, ht_delete, ht_list_all
from phonebook import bst_insert, bst_find, bst_delete, bst_list_all
def generate_records(n, seed=42):
random.seed(seed)
recs = []
for i in range(1, n+1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
recs.append((name, phone))
return recs
def prepare_datasets(recs):
shuffled = recs.copy()
random.shuffle(shuffled)
sorted_recs = sorted(recs, key=lambda x: x[0])
return shuffled, sorted_recs
def measure_structure(create_func, insert_func, find_func, delete_func, records, repeats=5):
insert_times = []
find_times = []
delete_times = []
existing_names = [name for name,_ in records]
search_names = random.sample(existing_names, 100) + [f"None_{i}" for i in range(10)]
random.shuffle(search_names)
delete_names = random.sample(existing_names, 50)
for _ in range(repeats):
struct = create_func()
# вставка
start = time.perf_counter()
for name, phone in records:
struct = insert_func(struct, name, phone)
insert_times.append(time.perf_counter() - start)
# поиск
start = time.perf_counter()
for name in search_names:
find_func(struct, name)
find_times.append(time.perf_counter() - start)
# удаление
start = time.perf_counter()
for name in delete_names:
struct = delete_func(struct, name)
delete_times.append(time.perf_counter() - start)
return insert_times, find_times, delete_times
def main():
N = 1000
base = generate_records(N)
shuffled, sorted_recs = prepare_datasets(base)
results = []
# Linked list
for mode, data in [('random', shuffled), ('sorted', sorted_recs)]:
ins, find, dele = measure_structure(lambda: None, ll_insert, ll_find, ll_delete, data)
for i in range(5):
results.append(['LinkedList', mode, 'insert', ins[i]])
results.append(['LinkedList', mode, 'find', find[i]])
results.append(['LinkedList', mode, 'delete', dele[i]])
# Hash table
for mode, data in [('random', shuffled), ('sorted', sorted_recs)]:
ins, find, dele = measure_structure(ht_create, ht_insert, ht_find, ht_delete, data)
for i in range(5):
results.append(['HashTable', mode, 'insert', ins[i]])
results.append(['HashTable', mode, 'find', find[i]])
results.append(['HashTable', mode, 'delete', dele[i]])
# BST
for mode, data in [('random', shuffled), ('sorted', sorted_recs)]:
ins, find, dele = measure_structure(lambda: None, bst_insert, bst_find, bst_delete, data)
for i in range(5):
results.append(['BST', mode, 'insert', ins[i]])
results.append(['BST', mode, 'find', find[i]])
results.append(['BST', mode, 'delete', dele[i]])
with open('results.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['Structure','Mode','Operation','Time_sec'])
writer.writerows(results)
print("Results saved to results.csv")
if __name__ == '__main__':
main()

View File

@ -1,217 +0,0 @@
# phonebook.py
# Узел списка: {'n': имя, 'p': телефон, 'nxt': следующий}
def ll_insert(head, name, phone):
# обновление, если уже есть
curr = head
while curr is not None:
if curr['n'] == name:
curr['p'] = phone
return head
curr = curr['nxt']
# вставка в начало (новый узел становится головой)
new_node = {'n': name, 'p': phone, 'nxt': head}
return new_node
def ll_find(head, name):
curr = head
while curr is not None:
if curr['n'] == name:
return curr['p']
curr = curr['nxt']
return None
def ll_delete(head, name):
if head is None:
return None
if head['n'] == name:
return head['nxt']
prev = head
curr = head['nxt']
while curr is not None:
if curr['n'] == name:
prev['nxt'] = curr['nxt']
return head
prev = curr
curr = curr['nxt']
return head
def ll_list_all(head):
records = []
curr = head
while curr is not None:
records.append((curr['n'], curr['p']))
curr = curr['nxt']
records.sort(key=lambda x: x[0])
return records
# хеш-функция: сумма ord(name) % size
def _hash(name, size):
h = 0
for ch in name:
h += ord(ch)
return h % size
SIZE = 13 # фиксированный размер таблицы
def ht_create():
return [None] * SIZE
def ht_insert(buckets, name, phone):
idx = _hash(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
return buckets
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)
return buckets
def ht_list_all(buckets):
all_records = []
for head in buckets:
curr = head
while curr:
all_records.append((curr['n'], curr['p']))
curr = curr['nxt']
all_records.sort(key=lambda x: x[0])
return all_records
# Узел дерева: {'n': имя, 'p': телефон, 'l': левый, 'r': правый}
def bst_create_node(name, phone):
return {'n': name, 'p': phone, 'l': None, 'r': None}
def bst_insert(root, name, phone):
if root is None:
return bst_create_node(name, phone)
# итеративная вставка (без рекурсии)
parent = None
cur = root
while cur:
parent = cur
if name == cur['n']:
cur['p'] = phone
return root
elif name < cur['n']:
cur = cur['l']
else:
cur = cur['r']
# вставляем как лист
if name < parent['n']:
parent['l'] = bst_create_node(name, phone)
else:
parent['r'] = bst_create_node(name, phone)
return root
def bst_find(root, name):
cur = root
while cur:
if name == cur['n']:
return cur['p']
elif name < cur['n']:
cur = cur['l']
else:
cur = cur['r']
return None
def _bst_min(node):
while node['l']:
node = node['l']
return node
def bst_delete(root, name):
if root is None:
return None
# поиск узла и родителя
parent = None
cur = root
while cur and cur['n'] != name:
parent = cur
if name < cur['n']:
cur = cur['l']
else:
cur = cur['r']
if cur is None:
return root
# случай 0 или 1 ребёнок
if cur['l'] is None or cur['r'] is None:
child = cur['l'] if cur['l'] else cur['r']
if parent is None:
return child
if parent['l'] == cur:
parent['l'] = child
else:
parent['r'] = child
else:
# два ребёнка - ищем inorder-преемника
succ_parent = cur
succ = cur['r']
while succ['l']:
succ_parent = succ
succ = succ['l']
cur['n'], cur['p'] = succ['n'], succ['p']
if succ_parent['l'] == succ:
succ_parent['l'] = succ['r']
else:
succ_parent['r'] = succ['r']
return root
def bst_list_all(root):
result = []
def inorder(node):
if node:
inorder(node['l'])
result.append((node['n'], node['p']))
inorder(node['r'])
inorder(root)
return result
# TESTING
if __name__ == '__main__':
print("=== Linked list test ===")
head = None
head = ll_insert(head, "Ivan", "111")
head = ll_insert(head, "Anna", "222")
head = ll_insert(head, "Ivan", "333")
print(ll_find(head, "Ivan")) # 333
print(ll_list_all(head)) # [('Anna','222'),('Ivan','333')]
head = ll_delete(head, "Anna")
print(ll_list_all(head)) # [('Ivan','333')]
print("\n=== Hash table test ===")
buckets = ht_create()
ht_insert(buckets, "Ivan", "111")
ht_insert(buckets, "Boris", "444")
print(ht_find(buckets, "Ivan")) # 111
print(ht_list_all(buckets)) # [('Boris','444'),('Ivan','111')]
print("\n=== BST test ===")
root = None
root = bst_insert(root, "Ivan", "111")
root = bst_insert(root, "Anna", "222")
root = bst_insert(root, "Ivan", "333")
print(bst_find(root, "Ivan")) # 333
print(bst_list_all(root)) # [('Anna','222'),('Ivan','333')]

View File

@ -1,35 +0,0 @@
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_csv('results.csv')
mean_df = df.groupby(['Structure','Mode','Operation'])['Time_sec'].mean().reset_index()
fig, axes = plt.subplots(1, 3, figsize=(14,5))
operations = ['insert','find','delete']
titles = ['Insertion', 'Search', 'Deletion']
for ax, op, title in zip(axes, operations, titles):
subset = mean_df[mean_df['Operation'] == op]
structures = subset['Structure'].unique()
x = np.arange(len(structures))
width = 0.35
random_vals = []
sorted_vals = []
for s in structures:
r = subset[(subset['Structure']==s) & (subset['Mode']=='random')]['Time_sec'].values
s_vals = subset[(subset['Structure']==s) & (subset['Mode']=='sorted')]['Time_sec'].values
random_vals.append(r[0] if len(r) else 0)
sorted_vals.append(s_vals[0] if len(s_vals) else 0)
ax.bar(x - width/2, random_vals, width, label='random')
ax.bar(x + width/2, sorted_vals, width, label='sorted')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Time (seconds)')
ax.set_title(title)
ax.legend()
plt.tight_layout()
plt.savefig('performance.png', dpi=150)
plt.show()
print("График сохранён (performance.png)")

View File

@ -1,91 +0,0 @@
Structure,Mode,Operation,Time_sec
LinkedList,random,insert,0.023610104999988835
LinkedList,random,find,0.0024258809999082587
LinkedList,random,delete,0.0009224560001257487
LinkedList,random,insert,0.03432773700001235
LinkedList,random,find,0.0028615219998755492
LinkedList,random,delete,0.0009829489999901853
LinkedList,random,insert,0.02187811499993586
LinkedList,random,find,0.002508116999933918
LinkedList,random,delete,0.0009394689998316608
LinkedList,random,insert,0.02058078499999283
LinkedList,random,find,0.0024640399999498186
LinkedList,random,delete,0.0009221469999829424
LinkedList,random,insert,0.021287126000061107
LinkedList,random,find,0.002533143000164273
LinkedList,random,delete,0.0009955239997907483
LinkedList,sorted,insert,0.020153931999857377
LinkedList,sorted,find,0.0025785160000850738
LinkedList,sorted,delete,0.0009765429999788466
LinkedList,sorted,insert,0.019765774000006786
LinkedList,sorted,find,0.002487556999994922
LinkedList,sorted,delete,0.0008901209998839477
LinkedList,sorted,insert,0.018835716000012326
LinkedList,sorted,find,0.0023183840000911005
LinkedList,sorted,delete,0.0009144370001195057
LinkedList,sorted,insert,0.019278175999943414
LinkedList,sorted,find,0.002386138000019855
LinkedList,sorted,delete,0.0009126009999818052
LinkedList,sorted,insert,0.01877526999987822
LinkedList,sorted,find,0.002359818000059022
LinkedList,sorted,delete,0.0009194389999720443
HashTable,random,insert,0.0023323159998653864
HashTable,random,find,0.0002526580001358525
HashTable,random,delete,0.00012695100008386362
HashTable,random,insert,0.0024649750000662607
HashTable,random,find,0.0002549820001149783
HashTable,random,delete,0.00012324999988777563
HashTable,random,insert,0.0023000859998774104
HashTable,random,find,0.00025735399981385854
HashTable,random,delete,0.0001301180000155
HashTable,random,insert,0.0022806430001764966
HashTable,random,find,0.00024959500001386914
HashTable,random,delete,0.00012412399996719614
HashTable,random,insert,0.0033660579999832407
HashTable,random,find,0.0003928979999727744
HashTable,random,delete,0.00013623100016957324
HashTable,sorted,insert,0.0025681740000891295
HashTable,sorted,find,0.00024172200005523337
HashTable,sorted,delete,0.00011611300010372361
HashTable,sorted,insert,0.0021931220001079055
HashTable,sorted,find,0.0002396149998276087
HashTable,sorted,delete,0.0001115909999498399
HashTable,sorted,insert,0.002177270999936809
HashTable,sorted,find,0.00026490999994166486
HashTable,sorted,delete,0.0001120919998811587
HashTable,sorted,insert,0.0021901160000652453
HashTable,sorted,find,0.0002393899999333371
HashTable,sorted,delete,0.00011373199981790094
HashTable,sorted,insert,0.0021746099998836144
HashTable,sorted,find,0.00024168799996004964
HashTable,sorted,delete,0.00011215499989702948
BST,random,insert,0.0011081129998729011
BST,random,find,9.674199986875465e-05
BST,random,delete,6.977399993957079e-05
BST,random,insert,0.0011156380001011712
BST,random,find,9.206000004269299e-05
BST,random,delete,6.480000001829467e-05
BST,random,insert,0.0010883550000926334
BST,random,find,8.914799991543987e-05
BST,random,delete,6.064600006538967e-05
BST,random,insert,0.0010896240000874968
BST,random,find,8.920699997361226e-05
BST,random,delete,6.108699994911149e-05
BST,random,insert,0.0010866299999179319
BST,random,find,8.843199998409546e-05
BST,random,delete,6.088700001782854e-05
BST,sorted,insert,0.035164145999942775
BST,sorted,find,0.003177170000071783
BST,sorted,delete,0.0018665320001218788
BST,sorted,insert,0.03501290000008339
BST,sorted,find,0.003258286999880511
BST,sorted,delete,0.0018976070000462641
BST,sorted,insert,0.03562600000009297
BST,sorted,find,0.0031255549999968935
BST,sorted,delete,0.0018366239999068057
BST,sorted,insert,0.03548556199984887
BST,sorted,find,0.003188709999903949
BST,sorted,delete,0.001886656000124276
BST,sorted,insert,0.035131116000002294
BST,sorted,find,0.0032029789999796776
BST,sorted,delete,0.0018500549999771465
1 Structure Mode Operation Time_sec
2 LinkedList random insert 0.023610104999988835
3 LinkedList random find 0.0024258809999082587
4 LinkedList random delete 0.0009224560001257487
5 LinkedList random insert 0.03432773700001235
6 LinkedList random find 0.0028615219998755492
7 LinkedList random delete 0.0009829489999901853
8 LinkedList random insert 0.02187811499993586
9 LinkedList random find 0.002508116999933918
10 LinkedList random delete 0.0009394689998316608
11 LinkedList random insert 0.02058078499999283
12 LinkedList random find 0.0024640399999498186
13 LinkedList random delete 0.0009221469999829424
14 LinkedList random insert 0.021287126000061107
15 LinkedList random find 0.002533143000164273
16 LinkedList random delete 0.0009955239997907483
17 LinkedList sorted insert 0.020153931999857377
18 LinkedList sorted find 0.0025785160000850738
19 LinkedList sorted delete 0.0009765429999788466
20 LinkedList sorted insert 0.019765774000006786
21 LinkedList sorted find 0.002487556999994922
22 LinkedList sorted delete 0.0008901209998839477
23 LinkedList sorted insert 0.018835716000012326
24 LinkedList sorted find 0.0023183840000911005
25 LinkedList sorted delete 0.0009144370001195057
26 LinkedList sorted insert 0.019278175999943414
27 LinkedList sorted find 0.002386138000019855
28 LinkedList sorted delete 0.0009126009999818052
29 LinkedList sorted insert 0.01877526999987822
30 LinkedList sorted find 0.002359818000059022
31 LinkedList sorted delete 0.0009194389999720443
32 HashTable random insert 0.0023323159998653864
33 HashTable random find 0.0002526580001358525
34 HashTable random delete 0.00012695100008386362
35 HashTable random insert 0.0024649750000662607
36 HashTable random find 0.0002549820001149783
37 HashTable random delete 0.00012324999988777563
38 HashTable random insert 0.0023000859998774104
39 HashTable random find 0.00025735399981385854
40 HashTable random delete 0.0001301180000155
41 HashTable random insert 0.0022806430001764966
42 HashTable random find 0.00024959500001386914
43 HashTable random delete 0.00012412399996719614
44 HashTable random insert 0.0033660579999832407
45 HashTable random find 0.0003928979999727744
46 HashTable random delete 0.00013623100016957324
47 HashTable sorted insert 0.0025681740000891295
48 HashTable sorted find 0.00024172200005523337
49 HashTable sorted delete 0.00011611300010372361
50 HashTable sorted insert 0.0021931220001079055
51 HashTable sorted find 0.0002396149998276087
52 HashTable sorted delete 0.0001115909999498399
53 HashTable sorted insert 0.002177270999936809
54 HashTable sorted find 0.00026490999994166486
55 HashTable sorted delete 0.0001120919998811587
56 HashTable sorted insert 0.0021901160000652453
57 HashTable sorted find 0.0002393899999333371
58 HashTable sorted delete 0.00011373199981790094
59 HashTable sorted insert 0.0021746099998836144
60 HashTable sorted find 0.00024168799996004964
61 HashTable sorted delete 0.00011215499989702948
62 BST random insert 0.0011081129998729011
63 BST random find 9.674199986875465e-05
64 BST random delete 6.977399993957079e-05
65 BST random insert 0.0011156380001011712
66 BST random find 9.206000004269299e-05
67 BST random delete 6.480000001829467e-05
68 BST random insert 0.0010883550000926334
69 BST random find 8.914799991543987e-05
70 BST random delete 6.064600006538967e-05
71 BST random insert 0.0010896240000874968
72 BST random find 8.920699997361226e-05
73 BST random delete 6.108699994911149e-05
74 BST random insert 0.0010866299999179319
75 BST random find 8.843199998409546e-05
76 BST random delete 6.088700001782854e-05
77 BST sorted insert 0.035164145999942775
78 BST sorted find 0.003177170000071783
79 BST sorted delete 0.0018665320001218788
80 BST sorted insert 0.03501290000008339
81 BST sorted find 0.003258286999880511
82 BST sorted delete 0.0018976070000462641
83 BST sorted insert 0.03562600000009297
84 BST sorted find 0.0031255549999968935
85 BST sorted delete 0.0018366239999068057
86 BST sorted insert 0.03548556199984887
87 BST sorted find 0.003188709999903949
88 BST sorted delete 0.001886656000124276
89 BST sorted insert 0.035131116000002294
90 BST sorted find 0.0032029789999796776
91 BST sorted delete 0.0018500549999771465

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,55 +0,0 @@
# Отчёт по лабе: телефонный справочник на трёх структурах
## Что делал
Реализовал три структуры для хранения записей (имя телефон) без классов, только словари и ссылки:
1. **Связный список** каждый узел `{'name': ..., 'phone': ..., 'next': ...}`.
Вставка в начало, перед этим проверка на дубликат (поиск по всему списку).
2. **Хеш-таблица** 13 корзин, в каждой связный список. Хеш-функция: сумма кодов символов `% 13`.
Вставка/поиск/удаление через хеш + вызов функций списка для конкретной корзины.
3. **Двоичное дерево поиска** узел `{'name': ..., 'phone': ..., 'left': ..., 'right': ...}`.
Вставка и поиск итеративные (циклы), удаление рекурсивное с поиском inorderпреемника.
Операции везде: `insert`, `find`, `delete`, `list_all` (для дерева обход по порядку, для остальных собрать всё в список и отсортировать).
## Эксперимент
Взял **1000 записей** вида `User_00001``User_01000`.
Подготовил два набора: случайный порядок и отсортированный по имени.
Для каждой структуры и каждого набора:
- Замерял время вставки всех 1000 записей (через `time.perf_counter()`).
- Затем поиск 110 имён (100 реальных + 10 вымышленных).
- Потом удаление 50 случайных записей.
Каждый замер повторял 5 раз, брал среднее.
Результаты сохранил в `results.csv`, потом построил график `performance.png`.
## Что получилось (график)
![performance](performance.png)
## Анализ
**BST**
На случайных данных работал очень быстро (логарифм). А на отсортированных ужасно: дерево выродилось в правую цепочку, высота стала 1000. Вставка замедлилась в ~58 раз, поиск и удаление тоже сильно просели. Это классическая проблема небалансированного дерева.
**Хеш-таблица**
Порядок данных почти не влияет. И в случайном, и в отсортированном режимах время одинаковое. Хеш-функция разбрасывает записи по корзинам, поэтому ей всё равно, откуда приходят данные.
**Связный список**
Ожидаемо медленный везде, потому что поиск всегда линейный (`O(n)`). Разницы между случайным и отсортированным нет список не умеет использовать порядок.
**Удаление** похоже на поиск по скорости, плюс чуть-чуть на перестановку ссылок. У хеш-таблицы удаление быстрее всего.
## Выводы
- **Хеш-таблица** лучший выбор, если нужен быстрый поиск и порядок вывода не важен. Стабильна и проста.
- **Двоичное дерево поиска** хороший вариант, если часто нужен отсортированный список, но **только при случайных данных**. Если данные могут прийти отсортированными, дерево сломается (станет как список). Надо брать сбалансированное (AVL, красно-чёрное).
- **Связный список** для реальной базы контактов не годится. Можно использовать только когда записей совсем мало (до сотни) или чисто в учебных целях.
Для телефонного справочника с тысячами записей я бы взял хеш-таблицу, а если надо часто выводить по алфавиту сбалансированное дерево.

1
MalkinMV/428b.md Normal file
View File

@ -0,0 +1 @@
428b

183
README.md
View File

@ -197,3 +197,186 @@ with open("results.csv", "w", newline="") as f:
- Как удаление работает в каждой структуре.
* Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.*
## Задание: Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)
### Цель работы
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
### Общая схема приложения (пример)
```mermaid
classDiagram
class Maze {
-Cell[] cells
-int width, height
-Cell start
-Cell exit
+getCell(x,y): Cell
+getNeighbors(cell): List~Cell~
}
class Cell {
-int x, y
-bool isWall
-bool isStart
-bool isExit
+isPassable(): bool
}
class MazeBuilder {
<<interface>>
+buildFromFile(filename): Maze
}
class TextFileMazeBuilder {
+buildFromFile(filename): Maze
}
class PathFindingStrategy {
<<interface>>
+findPath(maze, start, exit): List~Cell~
}
class BFSStrategy
class DFSStrategy
class AStarStrategy
class DijkstraStrategy
class SearchStats {
+timeMs: float
+visitedCells: int
+pathLength: int
}
class MazeSolver {
-Maze maze
-PathFindingStrategy strategy
+setStrategy(strategy)
+solve(): SearchStats
}
class Command {
<<interface>>
+execute()
+undo()
}
class MoveCommand {
-Player player
-Direction dir
-Cell previousCell
+execute()
+undo()
}
class Player {
-Cell currentCell
+moveTo(cell)
}
class Observer {
<<interface>>
+update(event)
}
class ConsoleView {
+update(event)
+render(maze, player, path)
}
MazeBuilder <|.. TextFileMazeBuilder
MazeBuilder --> Maze : creates
PathFindingStrategy <|.. BFSStrategy
PathFindingStrategy <|.. DFSStrategy
PathFindingStrategy <|.. AStarStrategy
PathFindingStrategy <|.. DijkstraStrategy
MazeSolver --> PathFindingStrategy : uses
MazeSolver --> Maze : uses
Command <|.. MoveCommand
MoveCommand --> Player
Player --> Cell
Observer <|.. ConsoleView
MazeSolver --> Observer : notifies
```
### Выполнение
#### Этап 1. Модель лабиринта (без паттернов, просто классы)
**Задача:** Создать классы `Cell` и `Maze`, которые представляют карту лабиринта.
- `Cell` хранит координаты (x, y), флаги `isWall`, `isStart`, `isExit`, метод `isPassable()` (возвращает `True` для прохода, если не стена).
- `Maze` хранит двумерный массив клеток, ширину, высоту, ссылки на стартовую и выходную клетку. Методы: `getCell(x, y)`, `getNeighbors(cell)` возвращает список соседних проходимых клеток (вверх, вниз, влево, вправо, если в пределах границ и не стена).
**Результат:** Лабиринт можно создать вручную в коде, но загрузку пока не делаем.
#### Этап 2. Загрузка лабиринта из файла применение паттерна **Builder**
**Задача:** Реализовать загрузку лабиринта из текстового файла, где `#` стена, ` ` (пробел) проход, `S` старт, `E` выход.
- Создать интерфейс `MazeBuilder` с методом `buildFromFile(filename)`.
- Реализовать класс `TextFileMazeBuilder`, который читает файл, парсит символы, создаёт объекты `Cell`, задаёт координаты и флаги, после чего возвращает готовый `Maze`.
Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. В будущем можно легко добавить другой формат (например, JSON или бинарный) через новую реализацию `MazeBuilder`.
#### Этап 3. Стратегии поиска пути паттерн **Strategy**
**Задача:** Реализовать семейство алгоритмов поиска пути от старта до выхода.
- Создать интерфейс `PathFindingStrategy` с методом `findPath(maze, start, exit)`, возвращающим список клеток пути (от старта до выхода включительно) или пустой список, если пути нет.
- Реализовать минимум 3 стратегии:
- **BFS** (поиск в ширину) гарантирует кратчайший путь по количеству шагов.
- **DFS** (поиск в глубину) быстрый, но не обязательно кратчайший.
- **A*** (с эвристикой, например, манхэттенское расстояние) компромисс между скоростью и оптимальностью.
- (Опционально) **Дейкстра** полезна для взвешенных лабиринтов, но в базовом варианте все шаги имеют вес 1, тогда она совпадает с BFS.
Каждая стратегия возвращает путь. Для BFS/DFS используйте очередь/стек, для A* приоритетную очередь (heapq). Важно: алгоритмы не должны модифицировать сам лабиринт, только читать состояние клеток.
Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. Новый алгоритм можно добавить, реализовав интерфейс.
#### Этап 4. Класс-оркестратор **MazeSolver** (использует Strategy)
**Задача:** Создать класс, который принимает лабиринт и стратегию, выполняет поиск и собирает статистику.
- `MazeSolver` содержит поля `maze` и `strategy`.
- Метод `setStrategy(strategy)` для динамической смены алгоритма.
- Метод `solve()` вызывает `strategy.findPath(...)` и возвращает объект `SearchStats` (время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути).
- Для замера времени используйте `time.perf_counter()` до и после вызова стратегии.
#### Этап 5. Визуализация и пошаговое управление паттерны **Observer** и **Command** (по желанию)
**5.1. Наблюдатель (Observer)** обновление консольного интерфейса.
- Создать интерфейс `Observer` с методом `update(event)`, где `event` может быть строкой или объектом с типом события (`"path_found"`, `"move"`, `"maze_loaded"`).
- Реализовать класс `ConsoleView`, который отображает лабиринт, текущее положение игрока (если реализован пошаговый режим) и найденный путь. Метод `render(maze, player_position, path)` рисует карту в консоли.
- `MazeSolver` (или отдельный контроллер) может иметь список наблюдателей и уведомлять их при изменении состояния.
**5.2. Команда (Command)** для пошагового перемещения игрока по найденному пути (или ручного управления).
- Создать интерфейс `Command` с методами `execute()` и `undo()`.
- Реализовать `MoveCommand`, который принимает игрока (`Player`), направление и изменяет его позицию, сохраняя предыдущую для отмены.
- Создать класс `Player`, хранящий текущую клетку.
- Консольное меню позволяет вводить команды (W/A/S/D), выполнять `MoveCommand`, при необходимости отменять последний ход (Ctrl+Z). Это опционально, но очень наглядно демонстрирует паттерн.
*Observer можно реализовать только для вывода сообщений о начале/конце поиска, а Command для демонстрации undo при ручном исследовании лабиринта.*
#### Этап 6. Экспериментальная часть (аналогично заданию со структурами данных)
**Задача:** Сравнить эффективность реализованных стратегий на лабиринтах разной сложности.
1. **Подготовка тестовых лабиринтов:**
- Маленький (10×10) с простым путём.
- Средний (50×50) с тупиками.
- Большой (100×100) с запутанной структурой.
- «Пустой» лабиринт (без стен) для демонстрации максимальной производительности.
- «Без выхода» чтобы проверить обработку отсутствия пути.
2. **Замеры:**
- Для каждого лабиринта и каждой стратегии запустить `solve()` 510 раз, усреднить время, количество посещённых клеток, длину пути.
- Записать результаты в CSV: `лабиринт,стратегия,время_мс,посещено_клеток,длина_пути`.
3. **Анализ:**
- Построить графики для каждого лабиринта.
- Проанализировать и написать выводы по итогам (эффективность того или иного алгоритма в разных случаях).
4. **Дополнительное задание:** Реализовать взвешенные клетки (например, болото вес 3, песок вес 2, асфальт вес 1) и сравнить Дейкстру с A* на взвешенном графе.
#### Этап 7. Отчёт
**Структура отчёта:**
1. Описание задачи и выбранных паттернов (с диаграммой классов из Mermaid).
2. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
3. Результаты экспериментов (таблицы, графики).
4. Анализ эффективности алгоритмов и применимости паттернов.
5. Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них.
### Советы
- Для A* самая простая эвристика: `abs(x1 - x2) + abs(y1 - y2)`.
- При поиске пути надо хранить предшественников (`parent` для каждой посещённой клетки), чтобы восстановить путь.
- Для BFS/DFS используй `deque` (очередь) и `list` (стек).
- Визуализацию в консоли можно сделать с помощью `os.system('cls' if os.name == 'nt' else 'clear')` для перерисовки.

0
Smirnovvs/428.txt Normal file
View File

0
VarnakovAA/429.md Normal file
View File

0
dyachenkoas/428 Normal file
View File

0
kalinovskiymi/428 Normal file
View File

6
osipovamd/428.md Normal file
View File

@ -0,0 +1,6 @@
{\rtf1\ansi\ansicpg1251\cocoartf2869
\cocoatextscaling0\cocoaplatform0{\fonttbl}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
}

1
victorovaas/429 Normal file
View File

@ -0,0 +1 @@
<EFBFBD>¥¦¨¬ ¢כ¢®₪  ×®¬ ­₪ ­  ם×א ­ (ECHO) ¢×«מח¥­.