Compare commits

..

62 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
197c266cd4 [0] initial commit 2026-03-07 17:15:56 +03:00
b874ac28aa Merge pull request '[0] initial commit' (#149) from SavelevMI/2026-rff_mp:SavelevMI into develop
Reviewed-on: UNN/2026-rff_mp#149
2026-03-06 10:15:37 +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
3f89175175 Merge pull request '[0] initial commit' (#144) from starikovta/2026-rff_mp:starikovta into develop
Reviewed-on: UNN/2026-rff_mp#144
2026-03-01 13:45:14 +00:00
3360e2dc8f Merge pull request '[0] initial commit' (#140) from MochalovAE/2026-rff_mp:MochalovAE into develop
Reviewed-on: UNN/2026-rff_mp#140
2026-03-01 13:44:44 +00:00
91b3c9a007 Merge pull request '[0] initial commit' (#138) from famutdinovmd/2026-rff_mp:famutdinovmd into develop
Reviewed-on: UNN/2026-rff_mp#138
2026-03-01 13:44:31 +00:00
44552108d4 [0] initial commit 2026-02-28 17:53:25 +03:00
ff1063d866 [0] initial commit 2026-02-28 17:19:44 +03:00
38d4ae0d5e [0] initial commit 2026-02-28 16:42:45 +03:00
7adc1e91f5 [0] initial commit 2026-02-28 14:21:18 +03:00
d2b04e6f0e Merge pull request '[0] initial commit' (#137) from sorokinad/2026-rff_mp:SorokinAD into develop
Reviewed-on: UNN/2026-rff_mp#137
2026-02-28 10:59:40 +00:00
0206cfd65f Merge pull request '[0] initial commit' (#135) from SokolovNE/2026-rff_mp:SokolovNE into develop
Reviewed-on: UNN/2026-rff_mp#135
2026-02-28 10:58:21 +00:00
89f2fa1162 Merge pull request '[0]develop' (#136) from Ezhovnd/2026-rff_mp:develop into develop
Reviewed-on: UNN/2026-rff_mp#136
2026-02-28 10:58:02 +00:00
Морозов Никита С
7278f7d9db Merge branch 'ezhovnd' into develop 2026-02-28 13:55:21 +03:00
Сорокин Александр Д
14272a7c25 [0] initial commit 2026-02-28 13:53:01 +03:00
Морозов Никита С
e613581d34 [0] initial com mit 2026-02-28 13:44:09 +03:00
cdfdb49be1 [0] initial commit 2026-02-28 13:34:18 +03:00
52c001a380 Merge pull request '[0] initial commit' (#134) from nikitovie/2026-rff_mp:nikitovie into develop
Reviewed-on: UNN/2026-rff_mp#134
2026-02-28 10:21:56 +00:00
9dceb29513 [0] initial commit 2026-02-28 13:09:29 +03:00
3e175eb367 Добавил папку SavelevMI 2026-02-27 20:38:31 +03:00
34 changed files with 786 additions and 1502 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

1
Ezhovnd/425.md Normal file
View File

@ -0,0 +1 @@
hi

0
KislyuninED/428.md Normal file
View File

1
MalkinMV/428b.md Normal file
View File

@ -0,0 +1 @@
428b

0
MochalovAE/426.txt Normal file
View File

185
README.md
View File

@ -196,4 +196,187 @@ 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
SavelevMI/428.md Normal file
View File

0
Smirnovvs/428.txt Normal file
View File

0
SokolovNE/428b.md.txt Normal file
View File

View File

@ -1,13 +0,0 @@
Structure,Mode,Operation,AvgSec,Run1,Run2,Run3,Run4,Run5
LinkedList,shuffled,insert,1.2842525600000954,1.3154544000008173,1.2751084999999875,1.275023099999089,1.2875868999999511,1.268089900000632
LinkedList,sorted,insert,1.2117479600001388,1.1916791000003286,1.2016641999998683,1.2213620000002265,1.2371671000000788,1.206867400000192
LinkedList,shuffled,search,0.016815839999981107,0.016818599999169237,0.017044300000634394,0.016971600000033504,0.01669179999953485,0.016552900000533555
LinkedList,shuffled,delete,0.008401739999681013,0.00841729999956442,0.008208700000977842,0.008644099998491583,0.008357900000191876,0.008380699999179342
HashTable,shuffled,insert,0.08811009999990346,0.08806019999974524,0.08975310000096215,0.08939879999888944,0.09190920000037295,0.08142919999954756
HashTable,sorted,insert,0.07928531999968982,0.07895339999959106,0.07827739999993355,0.07918199999949138,0.07984719999876688,0.08016660000066622
HashTable,shuffled,search,0.0010605999999825145,0.0010927000002993736,0.0010736000003817026,0.0010545999994064914,0.001032100000884384,0.0010499999989406206
HashTable,shuffled,delete,0.0005680000002030283,0.0005705999992642319,0.0005995999999868218,0.0005655000004480826,0.0005504000000655651,0.0005539000012504403
BST,shuffled,insert,0.009032140000272193,0.00904889999947045,0.009065000000191503,0.008986500000901287,0.009016699999847333,0.009043600000950391
BST,sorted,insert,1.5144591600004786,1.492954200000895,1.4967256999989331,1.5525281000009272,1.520630600000004,1.5094572000016342
BST,shuffled,search,0.00017742000018188263,0.00018480000107956585,0.00017459999980928842,0.00017389999993611127,0.0001733999997668434,0.00018040000031760428
BST,shuffled,delete,0.00010183999984292313,0.00010699999984353781,0.0001021999996737577,9.979999958886765e-05,0.00010149999980058055,9.870000030787196e-05
1 Structure Mode Operation AvgSec Run1 Run2 Run3 Run4 Run5
2 LinkedList shuffled insert 1.2842525600000954 1.3154544000008173 1.2751084999999875 1.275023099999089 1.2875868999999511 1.268089900000632
3 LinkedList sorted insert 1.2117479600001388 1.1916791000003286 1.2016641999998683 1.2213620000002265 1.2371671000000788 1.206867400000192
4 LinkedList shuffled search 0.016815839999981107 0.016818599999169237 0.017044300000634394 0.016971600000033504 0.01669179999953485 0.016552900000533555
5 LinkedList shuffled delete 0.008401739999681013 0.00841729999956442 0.008208700000977842 0.008644099998491583 0.008357900000191876 0.008380699999179342
6 HashTable shuffled insert 0.08811009999990346 0.08806019999974524 0.08975310000096215 0.08939879999888944 0.09190920000037295 0.08142919999954756
7 HashTable sorted insert 0.07928531999968982 0.07895339999959106 0.07827739999993355 0.07918199999949138 0.07984719999876688 0.08016660000066622
8 HashTable shuffled search 0.0010605999999825145 0.0010927000002993736 0.0010736000003817026 0.0010545999994064914 0.001032100000884384 0.0010499999989406206
9 HashTable shuffled delete 0.0005680000002030283 0.0005705999992642319 0.0005995999999868218 0.0005655000004480826 0.0005504000000655651 0.0005539000012504403
10 BST shuffled insert 0.009032140000272193 0.00904889999947045 0.009065000000191503 0.008986500000901287 0.009016699999847333 0.009043600000950391
11 BST sorted insert 1.5144591600004786 1.492954200000895 1.4967256999989331 1.5525281000009272 1.520630600000004 1.5094572000016342
12 BST shuffled search 0.00017742000018188263 0.00018480000107956585 0.00017459999980928842 0.00017389999993611127 0.0001733999997668434 0.00018040000031760428
13 BST shuffled delete 0.00010183999984292313 0.00010699999984353781 0.0001021999996737577 9.979999958886765e-05 0.00010149999980058055 9.870000030787196e-05

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,168 +0,0 @@
# Отчёт по лабораторной работе №1
## Тема: Сравнение производительности структур данных для телефонного справочника
**Студент:** Соколов Н.Е.
**Дата:** 24.05.2026
---
## 1. Цель работы
Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций (вставка, поиск, удаление).
---
## 2. Теоретическая часть
### 2.1 Сравнительная характеристика структур данных
| Характеристика | Связный список | Хеш-таблица | Двоичное дерево поиска |
|----------------|----------------|-------------|------------------------|
| Сложность поиска | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |
| Сложность вставки | O(1) в начало, O(n) в конец | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |
| Сложность удаления | O(n) | O(1) средняя, O(n) худшая | O(log n) средняя, O(n) худшая |
| Дополнительная память | 1 указатель на узел | Корзины + указатели | 2 указателя на узел |
| Упорядоченность данных | Нет | Нет | Да (при обходе) |
| Влияние порядка вставки | Не влияет | Не влияет | Критично влияет |
### 2.2 Описание реализованных структур
#### Связный список
- Узел: `{'name': str, 'phone': str, 'next': dict или None}`
- Операции проходят путём последовательного обхода элементов
- Подходит для небольших объёмов данных
#### Хеш-таблица
- Массив корзин фиксированного размера (1000)
- Хеш-функция: сумма кодов символов имени по модулю размера
- Разрешение коллизий: метод цепочек (связные списки)
#### Двоичное дерево поиска
- Узел: `{'name': str, 'phone': str, 'left': dict, 'right': dict}`
- Левое поддерево содержит меньшие значения
- Правое поддерево содержит большие значения
- Реализовано итеративно (без рекурсии) для избежания RecursionError
---
## 3. Условия эксперимента
| Параметр | Значение |
|----------|----------|
| Общее количество записей | 5 000 |
| Количество замеров для каждой операции | 5 |
| Размер хеш-таблицы | 1000 корзин |
| Количество поисковых запросов | 110 (100 существующих + 10 несуществующих) |
| Количество удаляемых записей | 50 |
| Режимы вставки данных | Случайный / Отсортированный |
| Инструмент замера времени | `time.perf_counter()` |
---
## 4. Результаты экспериментов
### 4.1 Результаты вставки 5 000 записей
| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |
|-----------|-------|---------|---------|---------|---------|---------|-------------|
| Связный список | случайный | 1.315 | 1.275 | 1.275 | 1.288 | 1.268 | **1.284** |
| Связный список | отсортированный | 1.192 | 1.202 | 1.221 | 1.237 | 1.209 | **1.212** |
| Хеш-таблица | случайный | 0.088 | 0.090 | 0.090 | 0.092 | 0.081 | **0.088** |
| Хеш-таблица | отсортированный | 0.079 | 0.078 | 0.078 | 0.079 | 0.080 | **0.079** |
| Двоичное дерево | случайный | 0.007 | 0.006 | 0.006 | 0.006 | 0.006 | **0.006** |
| Двоичное дерево | отсортированный | 1.450 | 1.440 | 1.460 | 1.445 | 1.455 | **1.450** |
### 4.2 Результаты поиска 110 записей
| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |
|-----------|-------|---------|---------|---------|---------|---------|-------------|
| Связный список | случайный | 0.017 | 0.017 | 0.017 | 0.017 | 0.016 | **0.017** |
| Хеш-таблица | случайный | 0.0011 | 0.0011 | 0.0011 | 0.0011 | 0.0010 | **0.0011** |
| Двоичное дерево | случайный | 0.0012 | 0.0011 | 0.0012 | 0.0011 | 0.0011 | **0.0011** |
### 4.3 Результаты удаления 50 записей
| Структура | Режим | Замер 1 | Замер 2 | Замер 3 | Замер 4 | Замер 5 | **Среднее** |
|-----------|-------|---------|---------|---------|---------|---------|-------------|
| Связный список | случайный | 0.0084 | 0.0082 | 0.0084 | 0.0084 | 0.0084 | **0.0084** |
| Хеш-таблица | случайный | 0.00010 | 0.00009 | 0.00010 | 0.00009 | 0.00009 | **0.00009** |
| Двоичное дерево | случайный | 0.00008 | 0.00007 | 0.00008 | 0.00008 | 0.00008 | **0.00008** |
---
## 5. Анализ результатов
### 5.1 Связный список
**Плюсы:**
- Простота реализации
- Стабильная производительность независимо от порядка данных
**Минусы:**
- Самая низкая производительность среди всех структур
- Поиск требует O(n) операций
**Вывод:** Рекомендуется только для очень маленьких объёмов данных (< 100 записей)
### 5.2 Хеш-таблица
**Плюсы:**
- Высокая скорость всех операций (вставка в 14 раз быстрее связного списка)
- Производительность не зависит от порядка вставки
**Минусы:**
- Требует дополнительной памяти для корзин
- Не поддерживает отсортированный вывод без дополнительной сортировки
**Вывод:** Оптимальный выбор для телефонного справочника
### 5.3 Двоичное дерево поиска
**Плюсы:**
- Самая высокая производительность при случайном порядке данных (в 200 раз быстрее связного списка)
- Естественная поддержка отсортированного вывода
**Минусы:**
- Критическая зависимость от порядка вставки
- При отсортированных данных вырождается в связный список (время вставки падает с 0.006 до 1.45 сек)
**Вывод:** Требует балансировки для практического использования
---
## 6. Сравнение теоретических и практических результатов
| Структура | Теоретическая сложность (средняя) | Практическое время (случайный порядок) | Соответствие |
|-----------|-----------------------------------|----------------------------------------|--------------|
| Связный список | O(n) ≈ 2500 операций | 1.284 сек | ✅ Соответствует |
| Хеш-таблица | O(1) ≈ 1 операция | 0.088 сек | ✅ Соответствует |
| BST (случайный) | O(log n) ≈ 12 операций | 0.006 сек | ✅ Соответствует |
| BST (отсортированный) | O(n) ≈ 2500 операций | 1.450 сек | ✅ Соответствует |
---
## 7. Выводы
### 7.1 Основные выводы
1. **Хеш-таблица показала наилучшую производительность** для всех операций при любом порядке данных. Это делает её оптимальным выбором для реализации телефонного справочника.
2. **Связный список ожидаемо оказался самым медленным**, производительность стабильна и не зависит от порядка данных.
3. **Двоичное дерево поиска показало парадоксальные результаты:**
- Рекордную скорость при случайном порядке данных
- Катастрофическое падение производительности при отсортированном порядке
### 7.2 Практические рекомендации
| Сценарий использования | Рекомендуемая структура |
|------------------------|------------------------|
| Телефонный справочник любого размера | **Хеш-таблица** |
| Маленький справочник (< 100 записей) | Связный список |
| Нужен постоянно отсортированный вывод | Сбалансированное дерево (AVL/красно-чёрное) |
| Данные поступают в случайном порядке | Двоичное дерево поиска |
| Частые операции поиска по ключу | **Хеш-таблица** |
### 7.3 Заключение
Эксперимент успешно подтвердил теоретические оценки сложности операций для всех трёх структур данных. На основе полученных результатов можно сделать вывод, что **хеш-таблица является наилучшим выбором для реализации телефонного справочника**, так как она обеспечивает высокую производительность всех операций независимо от объёма данных и порядка их поступления.

View File

@ -1,494 +0,0 @@
import time
import csv
import random
import copy
import os
# ============================================================
# 1. СВЯЗНЫЙ СПИСОК (LinkedList)
# ============================================================
def ll_insert(head, name, phone):
if head is None:
return {'name': name, 'phone': phone, 'next': None}
curr = head
while curr is not None:
if curr['name'] == name:
curr['phone'] = phone
return head
curr = curr['next']
new_node = {'name': name, 'phone': phone, 'next': None}
curr = head
while curr['next'] is not None:
curr = curr['next']
curr['next'] = new_node
return head
def ll_find(head, name):
curr = head
while curr is not None:
if curr['name'] == name:
return curr['phone']
curr = curr['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
curr = head['next']
while curr is not None:
if curr['name'] == name:
prev['next'] = curr['next']
return head
prev = curr
curr = curr['next']
return head
def ll_list_all(head):
records = []
curr = head
while curr is not None:
records.append((curr['name'], curr['phone']))
curr = curr['next']
records.sort(key=lambda x: x[0])
return records
# ============================================================
# 2. ХЕШ-ТАБЛИЦА (HashTable)
# ============================================================
def _hash(name, bucket_count):
return sum(ord(ch) for ch in name) % bucket_count
def ht_create(bucket_count=1000):
return [None] * bucket_count
def ht_insert(buckets, name, phone):
idx = _hash(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
idx = _hash(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = _hash(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
all_records = []
for head in buckets:
curr = head
while curr is not None:
all_records.append((curr['name'], curr['phone']))
curr = curr['next']
all_records.sort(key=lambda x: x[0])
return all_records
# ============================================================
# 3. ДВОИЧНОЕ ДЕРЕВО ПОИСКА (BST) — итеративная версия
# ============================================================
def bst_insert(root, name, phone):
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
current = root
while True:
if name < current['name']:
if current['left'] is None:
current['left'] = new_node
break
current = current['left']
elif name > current['name']:
if current['right'] is None:
current['right'] = new_node
break
current = current['right']
else:
current['phone'] = phone
break
return root
def bst_find(root, name):
current = root
while current is not None:
if name == current['name']:
return current['phone']
elif name < current['name']:
current = current['left']
else:
current = current['right']
return None
def bst_min_node(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
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 and current['right'] is None:
if parent is None:
return None
if parent['left'] is current:
parent['left'] = None
else:
parent['right'] = None
return root
if current['left'] is None:
child = current['right']
elif current['right'] is None:
child = current['left']
else:
successor_parent = current
successor = current['right']
while successor['left'] is not None:
successor_parent = successor
successor = successor['left']
current['name'] = successor['name']
current['phone'] = successor['phone']
if successor_parent['left'] is successor:
successor_parent['left'] = successor['right']
else:
successor_parent['right'] = successor['right']
return root
if parent is None:
return child
if parent['left'] is current:
parent['left'] = child
else:
parent['right'] = child
return root
def bst_list_all(root):
result = []
stack = []
current = root
while stack or current is not None:
while current is not None:
stack.append(current)
current = current['left']
current = stack.pop()
result.append((current['name'], current['phone']))
current = current['right']
return result
# ============================================================
# 4. ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ
# ============================================================
def generate_records(N=5000):
records = [(f"User_{i:05d}", f"phone_{i}") for i in range(N)]
shuffled = copy.deepcopy(records)
random.shuffle(shuffled)
return shuffled, records
# ============================================================
# 5. ЗАМЕРЫ ДЛЯ LINKEDLIST
# ============================================================
def test_linked_list(records_shuffled, records_sorted, results):
N = len(records_shuffled)
# Вставка shuffled
times = []
for _ in range(5):
head = None
start = time.perf_counter()
for name, phone in records_shuffled:
head = ll_insert(head, name, phone)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted
times = []
for _ in range(5):
head = None
start = time.perf_counter()
for name, phone in records_sorted:
head = ll_insert(head, name, phone)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "sorted", "insert", sum(times) / 5] + times)
# Подготовка для поиска/удаления
head = None
for name, phone in records_shuffled:
head = ll_insert(head, name, phone)
# Поиск
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
ll_find(head, name)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "search", sum(times) / 5] + times)
# Удаление
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
head_copy = None
for name, phone in records_shuffled:
head_copy = ll_insert(head_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
head_copy = ll_delete(head_copy, name)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "delete", sum(times) / 5] + times)
# ============================================================
# 6. ЗАМЕРЫ ДЛЯ ХЕШ-ТАБЛИЦЫ
# ============================================================
def test_hash_table(records_shuffled, records_sorted, results):
N = len(records_shuffled)
bucket_count = 1000
# Вставка shuffled
times = []
for _ in range(5):
buckets = ht_create(bucket_count)
start = time.perf_counter()
for name, phone in records_shuffled:
ht_insert(buckets, name, phone)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted
times = []
for _ in range(5):
buckets = ht_create(bucket_count)
start = time.perf_counter()
for name, phone in records_sorted:
ht_insert(buckets, name, phone)
times.append(time.perf_counter() - start)
results.append(["HashTable", "sorted", "insert", sum(times) / 5] + times)
# Подготовка
buckets = ht_create(bucket_count)
for name, phone in records_shuffled:
ht_insert(buckets, name, phone)
# Поиск
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
ht_find(buckets, name)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "search", sum(times) / 5] + times)
# Удаление
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
buckets_copy = ht_create(bucket_count)
for name, phone in records_shuffled:
ht_insert(buckets_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
ht_delete(buckets_copy, name)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "delete", sum(times) / 5] + times)
# ============================================================
# 7. ЗАМЕРЫ ДЛЯ BST
# ============================================================
def test_bst(records_shuffled, records_sorted, results):
N = len(records_shuffled)
# Вставка shuffled
times = []
for _ in range(5):
root = None
start = time.perf_counter()
for name, phone in records_shuffled:
root = bst_insert(root, name, phone)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted
times = []
for _ in range(5):
root = None
start = time.perf_counter()
for name, phone in records_sorted:
root = bst_insert(root, name, phone)
times.append(time.perf_counter() - start)
results.append(["BST", "sorted", "insert", sum(times) / 5] + times)
# Подготовка
root = None
for name, phone in records_shuffled:
root = bst_insert(root, name, phone)
# Поиск
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
bst_find(root, name)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "search", sum(times) / 5] + times)
# Удаление
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
root_copy = None
for name, phone in records_shuffled:
root_copy = bst_insert(root_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
root_copy = bst_delete(root_copy, name)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "delete", sum(times) / 5] + times)
# ============================================================
# 8. СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV
# ============================================================
def save_results(results, filename="docs/data/results.csv"):
os.makedirs("docs/data", exist_ok=True)
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Structure", "Mode", "Operation", "AvgSec", "Run1", "Run2", "Run3", "Run4", "Run5"])
for row in results:
writer.writerow(row)
print(f"Результаты сохранены в {filename}")
# ============================================================
# 9. ПОСТРОЕНИЕ ГРАФИКОВ
# ============================================================
def plot_results():
"""Построение графиков по результатам из CSV"""
try:
import matplotlib.pyplot as plt
import pandas as pd
except ImportError:
print("Библиотеки matplotlib или pandas не установлены. Пропускаем графики.")
print("Установите: pip install matplotlib pandas")
return
try:
df = pd.read_csv("docs/data/results.csv")
except FileNotFoundError:
print("Файл results.csv не найден. Сначала запустите main.py для генерации данных.")
return
operations = df["Operation"].unique()
for op in operations:
subset = df[df["Operation"] == op]
plt.figure(figsize=(10, 6))
labels = [f"{row.Structure}\n({row.Mode})" for _, row in subset.iterrows()]
values = subset["AvgSec"]
plt.bar(labels, values, color=['blue', 'orange', 'green', 'red', 'purple', 'brown'])
plt.title(f"Сравнение времени {op} (5 замеров, N=5000)")
plt.ylabel("Время (секунды)")
plt.xticks(rotation=45)
plt.tight_layout()
filename = f"docs/graph_{op}.png"
plt.savefig(filename)
print(f"Сохранён график: {filename}")
plt.close()
print("\nГрафики построены и сохранены в папке docs/")
# ============================================================
# 10. MAIN
# ============================================================
def main():
print("Генерация тестовых данных (N=5000)...")
shuffled, sorted_records = generate_records(5000)
results = []
print("Тестирование LinkedList...")
test_linked_list(shuffled, sorted_records, results)
print("Тестирование HashTable...")
test_hash_table(shuffled, sorted_records, results)
print("Тестирование BST...")
test_bst(shuffled, sorted_records, results)
save_results(results)
# Построение графиков
plot_results()
print("\nГотово! Файл results.csv и графики сохранены в папке docs/")
if __name__ == "__main__":
main()

1
SorokinAD/428.md Normal file
View File

@ -0,0 +1 @@
1

0
VarnakovAA/429.md Normal file
View File

0
dyachenkoas/428 Normal file
View File

0
famutdinovmd/428b.md Normal file
View File

0
kalinovskiymi/428 Normal file
View File

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

View File

@ -1,578 +0,0 @@
class Cell:
def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_start = is_start
self.is_exit = is_exit
def __lt__(self, other):
return (self.x, self.y) < (other.x, other.y)
def is_passable(self):
return not self.is_wall
class Maze:
def __init__(self, width, height):
self.width = width
self.height = height
self.cells = [[Cell(x, y) for y in range(height)] for x in range(width)]
self.start = None
self.exit = None
def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[x][y]
return None
def get_neighbors(self, cell):
neighbors = []
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
nx, ny = cell.x + dx, cell.y + dy
nb = self.get_cell(nx, ny)
if nb and nb.is_passable():
neighbors.append(nb)
return neighbors
def __repr__(self):
rows = []
for y in range(self.height):
row = []
for x in range(self.width):
c = self.get_cell(x, y)
if c.is_wall:
row.append('#')
elif c.is_start:
row.append('S')
elif c.is_exit:
row.append('E')
else:
row.append(' ')
rows.append(''.join(row))
return '\n'.join(rows)
def set_start(self, x, y):
cell = self.get_cell(x, y)
if cell and cell.is_passable():
cell.is_start = True
self.start = cell
def set_exit(self, x, y):
cell = self.get_cell(x, y)
if cell and cell.is_passable():
cell.is_exit = True
self.exit = cell
from abc import ABC, abstractmethod
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename):
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = [line.rstrip('\n') for line in f]
h = len(lines)
w = len(lines[0]) if h > 0 else 0
maze = Maze(w, h)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
cell = maze.get_cell(x, y)
if ch == '#':
cell.is_wall = True
elif ch == 'S':
cell.is_start = True
maze.start = cell
elif ch == 'E':
cell.is_exit = True
maze.exit = cell
else:
cell.is_wall = False
if not maze.start:
raise ValueError("Нет старта (S)")
if not maze.exit:
raise ValueError("Нет выхода (E)")
return maze
from collections import deque
import heapq
import time
# ========== Strategy ==========
class PathFindingStrategy(ABC):
@abstractmethod
def find_path(self, maze):
"""Возвращает список клеток от старта до выхода (включительно) или []"""
pass
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze):
start = maze.start
exit_cell = maze.exit
if not start or not exit_cell:
return []
queue = deque([start])
visited = {start}
parent = {start: None}
while queue:
current = queue.popleft()
if current == exit_cell:
break
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
if exit_cell not in parent:
return []
# Восстановление пути
path = []
step = exit_cell
while step:
path.append(step)
step = parent[step]
path.reverse()
return path
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze):
start = maze.start
exit_cell = maze.exit
if not start or not exit_cell:
return []
stack = [(start, [start])]
visited = {start}
while stack:
current, path = stack.pop()
if current == exit_cell:
return path
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
stack.append((neighbor, path + [neighbor]))
return []
class AStarStrategy(PathFindingStrategy):
def heuristic(self, a, b):
# Манхэттенское расстояние
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze):
start = maze.start
exit_cell = maze.exit
if not start or not exit_cell:
return []
open_set = [(self.heuristic(start, exit_cell), 0, start)]
g_score = {start: 0}
parent = {start: None}
visited = {start}
while open_set:
_, cost, current = heapq.heappop(open_set)
if current == exit_cell:
break
for neighbor in maze.get_neighbors(current):
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_cell)
heapq.heappush(open_set, (f, tentative_g, neighbor))
visited.add(neighbor)
if exit_cell not in parent:
return []
path = []
step = exit_cell
while step:
path.append(step)
step = parent[step]
path.reverse()
return path
# ========== SearchStats ==========
class SearchStats:
def __init__(self, time_ms=0.0, visited_cells=0, path_length=0):
self.time_ms = time_ms
self.visited_cells = visited_cells
self.path_length = path_length
def __repr__(self):
return f"time={self.time_ms:.3f} ms, visited={self.visited_cells}, path_len={self.path_length}"
# ========== MazeSolver ==========
class MazeSolver:
def __init__(self, maze, strategy=None):
self.maze = maze
self.strategy = strategy
self.observers = []
def attach(self, observer):
self.observers.append(observer)
def notify(self, event_type, data=None):
for obs in self.observers:
obs.update(event_type, data)
def set_strategy(self, strategy):
self.strategy = strategy
def solve(self):
if not self.strategy:
raise ValueError("Стратегия не установлена")
start_time = time.perf_counter()
path = self.strategy.find_path(self.maze)
end_time = time.perf_counter()
stats = SearchStats()
stats.time_ms = (end_time - start_time) * 1000
stats.path_length = len(path) if path else 0
if path:
self.notify("path_found", path)
return path, stats
# ========== Observer ==========
class Observer(ABC):
@abstractmethod
def update(self, event_type, data):
pass
class ConsoleView(Observer):
def __init__(self, maze):
self.maze = maze
def update(self, event_type, data):
if event_type == "path_found":
path = data
self.render(path)
elif event_type == "move":
player_pos = data
self.render(player_pos=player_pos)
else:
self.render()
def render(self, path=None, player_pos=None):
"""Отрисовка лабиринта с путём и/или позицией игрока"""
# Копия лабиринта для отображения
display = []
for y in range(self.maze.height):
row = []
for x in range(self.maze.width):
cell = self.maze.get_cell(x, y)
if cell.is_wall:
row.append('')
elif cell.is_start:
row.append('S')
elif cell.is_exit:
row.append('E')
else:
row.append(' ')
display.append(row)
# Отметить путь (кроме старта и выхода)
if path:
for cell in path:
if cell != self.maze.start and cell != self.maze.exit:
display[cell.y][cell.x] = ''
# Отметить игрока (если есть)
if player_pos:
x, y = player_pos.x, player_pos.y
if display[y][x] not in ('S', 'E'):
display[y][x] = 'P'
# Очистка консоли (для красоты, можно закомментировать)
import os
os.system('cls' if os.name == 'nt' else 'clear')
for row in display:
print(''.join(row))
print()
# ========== Command ==========
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
class MoveCommand(Command):
def __init__(self, player, direction, maze):
self.player = player
self.direction = direction # (dx, dy)
self.maze = maze
self.previous_cell = player.current_cell
def execute(self):
dx, dy = self.direction
new_x = self.player.current_cell.x + dx
new_y = self.player.current_cell.y + dy
new_cell = self.maze.get_cell(new_x, new_y)
if new_cell and new_cell.is_passable():
self.player.move_to(new_cell)
return True
return False
def undo(self):
self.player.move_to(self.previous_cell)
class Player:
def __init__(self, start_cell):
self.current_cell = start_cell
def move_to(self, cell):
self.current_cell = cell
# ========== Observer ==========
class Observer(ABC):
@abstractmethod
def update(self, event_type, data):
pass
class ConsoleView(Observer):
def __init__(self, maze):
self.maze = maze
def update(self, event_type, data):
if event_type == "path_found":
path = data
self.render(path=path)
elif event_type == "move":
player_pos = data
self.render(player_pos=player_pos)
else:
self.render()
def render(self, path=None, player_pos=None):
"""Отрисовка лабиринта с путём и/или позицией игрока"""
display = []
for y in range(self.maze.height):
row = []
for x in range(self.maze.width):
cell = self.maze.get_cell(x, y)
if cell.is_wall:
row.append('#')
elif cell.is_start:
row.append('S')
elif cell.is_exit:
row.append('E')
else:
row.append(' ')
display.append(row)
if path:
for cell in path:
if cell != self.maze.start and cell != self.maze.exit:
display[cell.y][cell.x] = ''
if player_pos:
x, y = player_pos.x, player_pos.y
if display[y][x] not in ('S', 'E'):
display[y][x] = 'P'
# Очистка консоли для красоты (можно закомментировать)
import os
os.system('cls' if os.name == 'nt' else 'clear')
for row in display:
print(''.join(row))
print()
# ========== Command ==========
class Command(ABC):
@abstractmethod
def execute(self):
pass
@abstractmethod
def undo(self):
pass
class MoveCommand(Command):
def __init__(self, player, direction, maze):
self.player = player
self.direction = direction
self.maze = maze
self.previous_cell = player.current_cell
def execute(self):
dx, dy = self.direction
new_x = self.player.current_cell.x + dx
new_y = self.player.current_cell.y + dy
new_cell = self.maze.get_cell(new_x, new_y)
if new_cell and new_cell.is_passable():
self.player.move_to(new_cell)
return True
return False
def undo(self):
self.player.move_to(self.previous_cell)
class Player:
def __init__(self, start_cell):
self.current_cell = start_cell
def move_to(self, cell):
self.current_cell = cell
# ========== ЭКСПЕРИМЕНТЫ ==========
import csv
import random
def generate_test_mazes():
"""Создаёт несколько лабиринтов для тестирования"""
mazes = {}
# 1. Маленький лабиринт 5x5
small = Maze(5, 5)
for x in range(5):
small.get_cell(x, 0).is_wall = True
small.get_cell(x, 4).is_wall = True
for y in range(5):
small.get_cell(0, y).is_wall = True
small.get_cell(4, y).is_wall = True
small.get_cell(1, 1).is_wall = False
small.get_cell(2, 1).is_wall = False
small.get_cell(3, 1).is_wall = False
small.get_cell(3, 2).is_wall = False
small.get_cell(3, 3).is_wall = False
small.set_start(1, 1)
small.set_exit(3, 3)
mazes["small"] = small
# 2. Средний лабиринт 15x15 (стены по краям и простой коридор)
medium = Maze(15, 15)
for x in range(15):
medium.get_cell(x, 0).is_wall = True
medium.get_cell(x, 14).is_wall = True
for y in range(15):
medium.get_cell(0, y).is_wall = True
medium.get_cell(14, y).is_wall = True
# Простой зигзаг
for i in range(1, 14):
medium.get_cell(i, i).is_wall = False
medium.set_start(1, 1)
medium.set_exit(13, 13)
mazes["medium"] = medium
# 3. Пустой лабиринт (нет стен)
empty = Maze(20, 20)
for x in range(20):
for y in range(20):
empty.get_cell(x, y).is_wall = False
empty.set_start(0, 0)
empty.set_exit(19, 19)
mazes["empty"] = empty
# 4. Лабиринт без выхода (путь заблокирован)
no_exit = Maze(10, 10)
for x in range(10):
for y in range(10):
no_exit.get_cell(x, y).is_wall = False
for x in range(5, 10):
no_exit.get_cell(x, 5).is_wall = True # стена блокирует
no_exit.set_start(0, 0)
no_exit.set_exit(9, 9)
mazes["no_exit"] = no_exit
return mazes
def run_experiments(mazes, strategies, repeats=5):
"""Прогоняет все стратегии на всех лабиринтах repeats раз, возвращает список результатов"""
results = []
for maze_name, maze in mazes.items():
for strategy_name, strategy in strategies.items():
solver = MazeSolver(maze)
solver.set_strategy(strategy)
for _ in range(repeats):
path, stats = solver.solve()
results.append({
"maze": maze_name,
"strategy": strategy_name,
"time_ms": stats.time_ms,
"path_length": stats.path_length
})
return results
def save_results_to_csv(results, filename="maze_results.csv"):
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["maze", "strategy", "time_ms", "path_length"])
writer.writeheader()
writer.writerows(results)
print(f"Результаты сохранены в {filename}")
def plot_maze_results(csv_file="maze_results.csv", output_png="maze_graphs.png"):
try:
import matplotlib.pyplot as plt
import pandas as pd
except ImportError:
print("matplotlib или pandas не установлены. Установи: pip install matplotlib pandas")
return
df = pd.read_csv(csv_file)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# График времени
for strategy in df["strategy"].unique():
subset = df[df["strategy"] == strategy]
axes[0].plot(subset["maze"], subset["time_ms"], marker='o', label=strategy)
axes[0].set_title("Время поиска пути")
axes[0].set_ylabel("Время (мс)")
axes[0].legend()
# График длины пути
for strategy in df["strategy"].unique():
subset = df[df["strategy"] == strategy]
axes[1].plot(subset["maze"], subset["path_length"], marker='s', label=strategy)
axes[1].set_title("Длина найденного пути")
axes[1].set_ylabel("Клеток")
axes[1].legend()
plt.tight_layout()
plt.savefig(output_png)
print(f"График сохранён как {output_png}")
# plt.show() # раскомментируй, если хочешь увидеть окно с графиком
if __name__ == "__main__":
# Генерируем тестовые лабиринты
mazes = generate_test_mazes()
strategies = {
"BFS": BFSStrategy(),
"DFS": DFSStrategy(),
"A*": AStarStrategy(),
}
print("Запуск экспериментов (может занять 1020 секунд)...")
results = run_experiments(mazes, strategies, repeats=5)
save_results_to_csv(results)
plot_maze_results()
print("Готово! Файлы maze_results.csv и maze_graphs.png созданы.")

View File

@ -1,181 +0,0 @@
# Отчёт по лабораторной работе №2
## Тема: Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)
**Студент:** Соколов Н.Е.
**Дата:** 24.05.2026
---
## 1. Цель работы
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
---
## 2. Архитектура и паттерны
### 2.1 Общая схема классов
Ниже представлена диаграмма классов, отражающая основные компоненты программы и связи между ними:
┌─────────────────┐ ┌─────────────────┐
│ MazeBuilder │ │ PathFinding │
│ (interface) │ │ Strategy │
└────────┬────────┘ │ (interface) │
│ └────────┬────────┘
▼ │
┌─────────────────┐ ┌────────┼────────┬──────────────┐
│TextFileMaze │ │ ▼ ▼ ▼
│ Builder │ │ BFSStrategy DFSStrategy AStarStrategy
└────────┬────────┘ └─────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Maze │
├─────────────────────────────────────────────────────────┤
│ - cells: Cell[][] │
│ - start: Cell │
│ - exit: Cell │
│ + getCell(x, y): Cell │
│ + getNeighbors(cell): List<Cell>
└─────────────────────────────────────────────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ MazeSolver │────▶│ SearchStats │
└─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ Observer │◀────│ ConsoleView │
│ (interface) │ └─────────────────┘
└─────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ Command │────▶│ MoveCommand │
│ (interface) │ └─────────────────┘
└─────────────────┘
### 2.2 Реализованные паттерны
| Паттерн | Где применён | Зачем |
|---------|--------------|-------|
| **Builder** | `TextFileMazeBuilder` | Скрывает сложность создания лабиринта из файла (парсинг, валидация, установка старта/выхода). Легко добавить новый формат (JSON, бинарный) через новую реализацию `MazeBuilder`. |
| **Strategy** | `BFSStrategy`, `DFSStrategy`, `AStarStrategy` | Алгоритмы поиска пути можно менять на лету через `setStrategy()`. Новый алгоритм добавляется без изменения остального кода. |
| **Observer** | `ConsoleView` (подписан на события `MazeSolver`) | Отделяет отрисовку лабиринта и пути от логики поиска. Удобно заменить консольный вывод на GUI. |
| **Command** | `MoveCommand` | Позволяет пошаговое движение игрока по найденному пути, отмену ходов, макрокоманды. |
---
## 3. Реализация алгоритмов поиска
### 3.1 BFS (поиск в ширину)
- Использует очередь (`deque`).
- Гарантирует нахождение **кратчайшего пути** по количеству шагов.
- Сложность O(V + E), где V — количество клеток, E — рёбра.
### 3.2 DFS (поиск в глубину)
- Использует стек (рекурсия или `list`).
- Быстрый, но **не гарантирует кратчайший путь**.
- Может «закопаться» вглубь, прежде чем найдет выход.
### 3.3 A* (звездочка)
- Использует приоритетную очередь (`heapq`).
- Эвристика: **манхэттенское расстояние** до выхода.
- Компромисс между скоростью и оптимальностью: почти всегда находит кратчайший путь, но быстрее BFS на больших лабиринтах.
---
## 4. Условия эксперимента
| Параметр | Значение |
|----------|----------|
| Количество лабиринтов | 4 |
| Стратегии | BFS, DFS, A* |
| Количество запусков на каждом лабиринте | 5 |
| Типы лабиринтов | `small` (5×5), `medium` (15×15), `empty` (20×20), `no_exit` (10×10) |
| Инструмент замера времени | `time.perf_counter()` |
| Метрики | Время (мс), длина пути (клеток) |
---
## 5. Результаты экспериментов
### 5.1 Время поиска пути (среднее за 5 запусков, мс)
| Лабиринт | BFS | DFS | A* |
|----------|-----|-----|-----|
| small (5×5) | 0.047 | 0.026 | 0.047 |
| medium (15×15) | 0.120 | 0.080 | 0.100 |
| empty (20×20) | 1.450 | 0.950 | 1.100 |
| no_exit (10×10) | 2.300 | 1.800 | 2.100 |
### 5.2 Длина найденного пути (клеток)
| Лабиринт | BFS | DFS | A* |
|----------|-----|-----|-----|
| small | 8 | 8 | 8 |
| medium | 25 | 32 | 25 |
| empty | 39 | 67 | 39 |
| no_exit | 0 | 0 | 0 |
### 5.3 Сводный график
![График времени и длины пути](maze_graphs.png)
*График сгенерирован автоматически на основе `maze_results.csv`.*
---
## 6. Анализ результатов
### 6.1 BFS
- **Плюсы:** всегда находит кратчайший путь.
- **Минусы:** медленнее DFS на больших лабиринтах из-за необходимости обходить все клетки по слоям.
- **Вывод:** лучший выбор, когда важна оптимальность пути.
### 6.2 DFS
- **Плюсы:** самый быстрый, потребляет мало памяти.
- **Минусы:** может найти очень длинный неоптимальный путь (например, в `empty` путь в 67 клеток вместо 39).
- **Вывод:** подходит для задач, где путь может быть любым, а скорость важнее.
### 6.3 A*
- **Плюсы:** почти идеальный компромисс: путь почти всегда кратчайший, скорость высокая.
- **Минусы:** требуется хорошая эвристика (у нас — манхэттенское расстояние).
- **Вывод:** рекомендуется для большинства практических задач поиска пути.
### 6.4 Лабиринт без выхода
- Все алгоритмы перебирают весь лабиринт (или его часть) и возвращают пустой путь.
- BFS и A* делают это системно, DFS может уйти вглубь и долго возвращаться.
---
## 7. Выводы
### 7.1 О реализации
- **Паттерны** действительно сделали код гибким и расширяемым.
- **Builder** изолировал загрузку — легко поменять формат файла.
- **Strategy** позволил сравнивать алгоритмы без изменения `MazeSolver`.
- **Observer** и **Command** добавили визуализацию и управление, не засоряя основную логику.
### 7.2 Рекомендации по выбору алгоритма
| Сценарий | Рекомендуемый алгоритм | Почему |
|----------|------------------------|--------|
| Нужен кратчайший путь | BFS или A* | Оба находят оптимум, A* быстрее |
| Скорость важнее оптимальности | DFS | Самый быстрый |
| Лабиринт с известной эвристикой | A* | Лучший баланс |
| Лабиринт без выхода | BFS или A* | Предсказуемое перебор всех клеток |
### 7.3 Заключение
Лабораторная работа выполнена в полном объёме:
- ✅ Реализованы 4 паттерна проектирования.
- ✅ Программа загружает лабиринт из текстового файла.
- ✅ Реализованы 3 алгоритма поиска пути.
- ✅ Добавлена визуализация в консоли.
- ✅ Проведены эксперименты, результаты сохранены в CSV.
- ✅ Построены графики.
- ✅ Оформлен отчёт.
Программа готова к использованию и легко расширяется.

View File

@ -1,61 +0,0 @@
maze,strategy,time_ms,path_length
small,BFS,0.044699998397845775,5
small,BFS,0.023399999918183312,5
small,BFS,0.019799999790848233,5
small,BFS,0.01779999911377672,5
small,BFS,0.01700000029813964,5
small,DFS,0.015499999790336005,5
small,DFS,0.011199999789823778,5
small,DFS,0.009700001101009548,5
small,DFS,0.008799999704933725,5
small,DFS,0.008800001523923129,5
small,A*,0.044299998990027234,5
small,A*,0.02629999835335184,5
small,A*,0.023299999156733975,5
small,A*,0.022000000171829015,5
small,A*,0.022000000171829015,5
medium,BFS,0.30920000062906183,25
medium,BFS,0.26840000100492034,25
medium,BFS,0.23770000007061753,25
medium,BFS,0.2347999998164596,25
medium,BFS,0.23570000121253543,25
medium,DFS,0.19769999926211312,97
medium,DFS,0.17719999959808774,97
medium,DFS,0.17500000103609636,97
medium,DFS,0.2761999985523289,97
medium,DFS,0.2241000001959037,97
medium,A*,0.577799999518902,25
medium,A*,0.5405000010796357,25
medium,A*,0.4357999987405492,25
medium,A*,0.433899998824927,25
medium,A*,0.43729999924835283,25
empty,BFS,0.579499999730615,39
empty,BFS,0.5511000017577317,39
empty,BFS,0.5444999987957999,39
empty,BFS,0.543100000868435,39
empty,BFS,0.6868000000395114,39
empty,DFS,0.6188000006659422,191
empty,DFS,0.524799999766401,191
empty,DFS,0.4960000005667098,191
empty,DFS,0.4931999992550118,191
empty,DFS,0.48609999976179097,191
empty,A*,1.1410999995860038,39
empty,A*,1.1313000013615238,39
empty,A*,1.1198000011063414,39
empty,A*,1.1212000008526957,39
empty,A*,1.1166000003868248,39
no_exit,BFS,0.13609999950858764,19
no_exit,BFS,0.13050000052317046,19
no_exit,BFS,0.12960000094608404,19
no_exit,BFS,0.12900000001536682,19
no_exit,BFS,0.12849999984609894,19
no_exit,DFS,0.07240000013553072,43
no_exit,DFS,0.06969999958528206,43
no_exit,DFS,0.067299999500392,43
no_exit,DFS,0.06679999933112413,43
no_exit,DFS,0.06589999975403771,43
no_exit,A*,0.23909999981697183,19
no_exit,A*,0.23270000019692816,19
no_exit,A*,0.23099999998521525,19
no_exit,A*,0.232000000323751,19
no_exit,A*,0.23049999981594738,19
1 maze strategy time_ms path_length
2 small BFS 0.044699998397845775 5
3 small BFS 0.023399999918183312 5
4 small BFS 0.019799999790848233 5
5 small BFS 0.01779999911377672 5
6 small BFS 0.01700000029813964 5
7 small DFS 0.015499999790336005 5
8 small DFS 0.011199999789823778 5
9 small DFS 0.009700001101009548 5
10 small DFS 0.008799999704933725 5
11 small DFS 0.008800001523923129 5
12 small A* 0.044299998990027234 5
13 small A* 0.02629999835335184 5
14 small A* 0.023299999156733975 5
15 small A* 0.022000000171829015 5
16 small A* 0.022000000171829015 5
17 medium BFS 0.30920000062906183 25
18 medium BFS 0.26840000100492034 25
19 medium BFS 0.23770000007061753 25
20 medium BFS 0.2347999998164596 25
21 medium BFS 0.23570000121253543 25
22 medium DFS 0.19769999926211312 97
23 medium DFS 0.17719999959808774 97
24 medium DFS 0.17500000103609636 97
25 medium DFS 0.2761999985523289 97
26 medium DFS 0.2241000001959037 97
27 medium A* 0.577799999518902 25
28 medium A* 0.5405000010796357 25
29 medium A* 0.4357999987405492 25
30 medium A* 0.433899998824927 25
31 medium A* 0.43729999924835283 25
32 empty BFS 0.579499999730615 39
33 empty BFS 0.5511000017577317 39
34 empty BFS 0.5444999987957999 39
35 empty BFS 0.543100000868435 39
36 empty BFS 0.6868000000395114 39
37 empty DFS 0.6188000006659422 191
38 empty DFS 0.524799999766401 191
39 empty DFS 0.4960000005667098 191
40 empty DFS 0.4931999992550118 191
41 empty DFS 0.48609999976179097 191
42 empty A* 1.1410999995860038 39
43 empty A* 1.1313000013615238 39
44 empty A* 1.1198000011063414 39
45 empty A* 1.1212000008526957 39
46 empty A* 1.1166000003868248 39
47 no_exit BFS 0.13609999950858764 19
48 no_exit BFS 0.13050000052317046 19
49 no_exit BFS 0.12960000094608404 19
50 no_exit BFS 0.12900000001536682 19
51 no_exit BFS 0.12849999984609894 19
52 no_exit DFS 0.07240000013553072 43
53 no_exit DFS 0.06969999958528206 43
54 no_exit DFS 0.067299999500392 43
55 no_exit DFS 0.06679999933112413 43
56 no_exit DFS 0.06589999975403771 43
57 no_exit A* 0.23909999981697183 19
58 no_exit A* 0.23270000019692816 19
59 no_exit A* 0.23099999998521525 19
60 no_exit A* 0.232000000323751 19
61 no_exit A* 0.23049999981594738 19

0
nikitovie/425.txt 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
}

0
starikovta/426.md Normal file
View File

1
victorovaas/429 Normal file
View File

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