forked from UNN/2026-rff_mp
Compare commits
252 Commits
task-1-dat
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| 82e988c965 | |||
| 58daf860ed | |||
| 566d89fda2 | |||
| c7229154ca | |||
| 6e4ae1835b | |||
|
|
25341dc814 | ||
| fe9ce65eb2 | |||
| 405d1e583b | |||
| 74807f5514 | |||
| 3a251f06c7 | |||
| e4c7e2d97a | |||
|
|
7e84caffc4 | ||
| 84e5d1e763 | |||
| f5b0fec46f | |||
| e4423a3be8 | |||
| eeb28d8b6a | |||
| b3e4b6149e | |||
| c98c8a472b | |||
|
|
666e6ecd41 | ||
| 062d3b983e | |||
| 197c266cd4 | |||
| b874ac28aa | |||
| 4cef8060a3 | |||
| 50798eb572 | |||
| 7067bfa12a | |||
| 64f43373df | |||
| da65d02bd7 | |||
| 0441c5076a | |||
| 28de33a83d | |||
| 49e91066d4 | |||
| bb28c3dd2f | |||
| 1ebec4223a | |||
| e868e94fcd | |||
| 0e84534109 | |||
| 8c642fea20 | |||
| 7dbd3075b6 | |||
| 050462d011 | |||
| ba244f24bb | |||
| 39fe26b3f1 | |||
| 5de2cca73e | |||
| 8c17e92dd8 | |||
| b1cb2491d8 | |||
| a5ee4d9ae5 | |||
| fb8f0c47f5 | |||
| 099ec9f5e3 | |||
| 3f89175175 | |||
| 3360e2dc8f | |||
| 91b3c9a007 | |||
| 44552108d4 | |||
| ff1063d866 | |||
| 38d4ae0d5e | |||
| 7adc1e91f5 | |||
| d2b04e6f0e | |||
| 0206cfd65f | |||
| 89f2fa1162 | |||
|
|
7278f7d9db | ||
|
|
14272a7c25 | ||
|
|
e613581d34 | ||
| cdfdb49be1 | |||
| 52c001a380 | |||
| 9dceb29513 | |||
| f409499159 | |||
| 59076d3df1 | |||
| 3dbf137752 | |||
| 918536d2ed | |||
| 812f0acac2 | |||
| 8a30aff913 | |||
| d157503ef2 | |||
| 8d4e9ebeca | |||
| ec9b3fb7c6 | |||
| a29c7d7af6 | |||
| ef189db30c | |||
| 110e13b31e | |||
| 5640088b5a | |||
| d2b26c6f9e | |||
| a48c0abf0d | |||
| 6ec811b963 | |||
| 5fc3c42694 | |||
| 16808ba847 | |||
| e19c3b7841 | |||
| 88aa5a412f | |||
| 61f966e163 | |||
| 8c4322f768 | |||
| b85b706169 | |||
| 8198cfb061 | |||
| 7b7e8e93d4 | |||
| 036c08c2d9 | |||
| 2bf7ca3c92 | |||
| 786881334d | |||
|
|
ddf8ef5105 | ||
| ab854a04dd | |||
| c121e51b1f | |||
| 3e175eb367 | |||
| 3e5ee4a5a4 | |||
| aa6fbb0692 | |||
|
|
9eedcecf0c | ||
| 2204ca3dc8 | |||
| 764e6a3a2c | |||
| ec48b13150 | |||
| 3cfd61e6cc | |||
|
|
57c811ece4 | ||
| 0d86929b62 | |||
| e442988725 | |||
| 1a041a4dac | |||
| af2f607a3b | |||
| 96532a99fb | |||
| 809f768703 | |||
| 154b9b8b65 | |||
| aeb608a5d3 | |||
| f541180fcf | |||
| 134b330145 | |||
| 8dc1d57252 | |||
|
|
d7a13c9fe6 | ||
| 8124ef4551 | |||
| 8744999b03 | |||
| 01f0e5dd88 | |||
|
|
0c990ece48 | ||
|
|
9af52f3f7b | ||
|
|
9e70450c50 | ||
|
|
a7323e3762 | ||
| 365f830e05 | |||
| 7fe5c11997 | |||
| 969b9ee469 | |||
| 0c6a8bd4db | |||
| b849e3a7bc | |||
| 2e0eec1543 | |||
| 17fc44d83e | |||
| 4b24d66ab7 | |||
|
|
48d37c6f77 | ||
| 4a26e7aa89 | |||
| e0eb3ae13a | |||
| f983a6fa1d | |||
|
|
356e4b53dd | ||
| b7cea24df4 | |||
| 8221562750 | |||
| 789d8ef3bb | |||
|
|
6e433a36ae | ||
| a50fd0bfc1 | |||
|
|
de2b98bc01 | ||
| c53b093c38 | |||
| dd3b126d62 | |||
| 048b211945 | |||
| 6d05295507 | |||
| 55d8ed29c6 | |||
| af2d62a446 | |||
| fe5a8bea89 | |||
|
|
8fb5738b50 | ||
| b4b3140603 | |||
|
|
90503c7b92 | ||
|
|
bcde039ddd | ||
| ddc6d9b877 | |||
| e1e08990e0 | |||
|
|
fe50036baf | ||
|
|
8f2e46907f | ||
|
|
afa3504609 | ||
|
|
ccb7c7a5da | ||
|
|
6ef4820d9e | ||
| c3131ce0f6 | |||
| 8a523c9afb | |||
| e8ea43f9b3 | |||
|
|
57c8ef048f | ||
| c774f9182a | |||
| 194d822b62 | |||
|
|
589787851b | ||
| b34b2b76c8 | |||
| 46f73eaa47 | |||
| 5eab591f1a | |||
| 768a06fbb6 | |||
| 141bb2bbb5 | |||
| f7b4f75488 | |||
| 802b72d10b | |||
| 0cd1012949 | |||
| 6b2f019b11 | |||
| 5964cc81ab | |||
| 9f395a7b9a | |||
| 362b487266 | |||
| 8a4eb2ab7b | |||
| cc5764f86a | |||
| c276d52949 | |||
| a40c5f579a | |||
| 2ea1b50bc7 | |||
| d5450e0063 | |||
| b35fa6725b | |||
| de62717491 | |||
| 902ad5ef53 | |||
|
|
343a06519a | ||
| a2621ad508 | |||
| 95f3cd28df | |||
| 57adafbebd | |||
|
|
b881e05410 | ||
| d33fd62603 | |||
| e5733fd15d | |||
| 8dff4985b9 | |||
| 0f92398229 | |||
| 8c3a030957 | |||
| 6c0751dc68 | |||
| 5f779a102c | |||
| ad692ac5b8 | |||
| b4daf9ea72 | |||
| 71cb098a69 | |||
| c46e1aa8e2 | |||
| 595a3518d9 | |||
|
|
b98c87e4d5 | ||
|
|
41ad3771e8 | ||
|
|
de77a1109d | ||
| a0653117cd | |||
| 4428a8e88d | |||
| 41ce5b60cf | |||
| 195de5c70f | |||
| 60ecbdb35d | |||
|
|
3eab4cc3aa | ||
| 73cad3b59f | |||
| 1de0cee11d | |||
| 306299755c | |||
| 5dcd06963c | |||
| 65fea33bd4 | |||
|
|
ee26f74767 | ||
| 7000ccc96c | |||
|
|
7db86ec769 | ||
|
|
bef613db62 | ||
| a3c03ab3b4 | |||
| aadad1272e | |||
| b168cd4f7b | |||
| 391d8a79d1 | |||
|
|
ee723b700c | ||
| 87996f3d85 | |||
| 2a6f10cbf9 | |||
|
|
f2d51f5bc5 | ||
|
|
cb7dfdcf0d | ||
|
|
288f562fa0 | ||
|
|
ed603fc6dd | ||
| 27e1e98bf4 | |||
| 487ff6d741 | |||
| 9ad53dbe15 | |||
| fd82609af4 | |||
|
|
0ff7405de9 | ||
| 2e2a48fd30 | |||
|
|
34872ab84e | ||
| 3694f25de8 | |||
| b16d1f6731 | |||
| 9f68a759d8 | |||
| ca2a7eb3b6 | |||
| 7fec6872a1 | |||
|
|
fb021ba018 | ||
|
|
1d508ceb9b | ||
| 914063a196 | |||
|
|
f6a224a94a | ||
|
|
41acf4dab2 | ||
| b5469155fa | |||
| b4efeaf13e | |||
| f15908a49d | |||
| aca0eb0c84 |
17
.gitignore
vendored
17
.gitignore
vendored
|
|
@ -7,6 +7,7 @@ __pycache__/
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
build/
|
||||||
|
|
@ -160,19 +161,3 @@ cython_debug/
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
|
||||||
# Игнорируем CSV
|
|
||||||
aufgabe-1-data-structures/results/timedata_*.csv
|
|
||||||
|
|
||||||
# разрешаем средние данные
|
|
||||||
!aufgabe-1-data-structures/results/aaverage_*.csv
|
|
||||||
|
|
||||||
# Мусор LaTeX
|
|
||||||
*.aux
|
|
||||||
*.synctex.gz
|
|
||||||
*.toc
|
|
||||||
*.log
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
0
BolonkinNM/426.md
Normal file
0
BolonkinNM/426.md
Normal file
0
BoriskovaDV/428.md
Normal file
0
BoriskovaDV/428.md
Normal file
0
BorisovMI/429.md
Normal file
0
BorisovMI/429.md
Normal file
0
BudakovIS/428.md
Normal file
0
BudakovIS/428.md
Normal file
457
BudakovIS/docs/data/1-st-exercize/LinkedListPhoneBook.py
Normal file
457
BudakovIS/docs/data/1-st-exercize/LinkedListPhoneBook.py
Normal 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()
|
||||||
31
BudakovIS/docs/data/1-st-exercize/experiment_results.csv
Normal file
31
BudakovIS/docs/data/1-st-exercize/experiment_results.csv
Normal 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
|
||||||
|
44
BudakovIS/docs/data/1-st-exercize/plot_results.py
Normal file
44
BudakovIS/docs/data/1-st-exercize/plot_results.py
Normal 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()
|
||||||
BIN
BudakovIS/docs/performance_comparison.png
Normal file
BIN
BudakovIS/docs/performance_comparison.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
60
BudakovIS/docs/report_1-st-exersize.md
Normal file
60
BudakovIS/docs/report_1-st-exersize.md
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Отчёт по лабораторной работе "Структуры данных"
|
||||||
|
|
||||||
|
## 1. Введение
|
||||||
|
В рамках работы были реализованы три структуры данных для хранения телефонного справочника: связный список, хеш-таблица и двоичное дерево поиска. Проведено экспериментальное сравнение производительности операций вставки, поиска и удаления на наборе из **10 000 записей**. Для каждой структуры тестирование выполнялось на двух вариантах входных данных: случайный порядок и отсортированный по имени. Каждый эксперимент повторялся 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 |
|
||||||
|
|
||||||
|
Графическое представление результатов приведено на рисунке ниже.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.000085–0.00014 с) значительно меньше, чем в списке.
|
||||||
|
- **BST**: на случайных данных удаление очень быстрое (0.000053 с) благодаря логарифмической высоте. На отсортированных данных время возрастает до 0.00268 с (в 50 раз), что отражает деградацию до O(n).
|
||||||
|
|
||||||
|
## 4. Выводы и рекомендации по выбору структуры
|
||||||
|
|
||||||
|
На основе полученных результатов можно сформулировать следующие рекомендации:
|
||||||
|
|
||||||
|
- **Хеш-таблица** – оптимальный выбор, если требуется максимальная скорость поиска, вставки и удаления, а порядок хранения не важен. Примеры: реализация словарей, кэшей, индексов по ключу. В эксперименте хеш-таблица показала стабильно высокую производительность во всех режимах.
|
||||||
|
|
||||||
|
- **Двоичное дерево поиска** – следует применять, когда необходимо получать данные в отсортированном порядке (например, вывод телефонного справочника по алфавиту). Однако важно учитывать, что при поступлении отсортированных данных дерево вырождается, и производительность резко падает. В таких случаях лучше использовать сбалансированные деревья (AVL, красно-чёрные). В эксперименте BST на случайных данных показал отличные результаты, близкие к хеш-таблице, а на отсортированных – стал самым медленным.
|
||||||
|
|
||||||
|
- **Связный список** – практически непригоден для больших объёмов данных из-за линейной сложности основных операций. Может использоваться лишь для очень маленьких коллекций, при частых вставках в начало списка (здесь не рассматривалось) или в учебных целях.
|
||||||
|
|
||||||
|
Таким образом, для реальных задач чаще всего выбирают хеш-таблицы или сбалансированные деревья в зависимости от требований к упорядоченности данных.
|
||||||
|
|
||||||
|
|
||||||
|
I use arch BTW
|
||||||
0
DerbenevRY/428.md
Normal file
0
DerbenevRY/428.md
Normal file
1
Ezhovnd/425.md
Normal file
1
Ezhovnd/425.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
hi
|
||||||
0
GorkinMM/425.md
Normal file
0
GorkinMM/425.md
Normal file
0
GutovVM/428b.md
Normal file
0
GutovVM/428b.md
Normal file
0
KislyuninED/428.md
Normal file
0
KislyuninED/428.md
Normal file
0
KolbasovPD/425.md
Normal file
0
KolbasovPD/425.md
Normal file
0
KorotkinSE/428b.md
Normal file
0
KorotkinSE/428b.md
Normal file
1
KuzminskiyAA/427.md
Normal file
1
KuzminskiyAA/427.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
0
KuznetsovAS/427.md
Normal file
0
KuznetsovAS/427.md
Normal file
0
KuznetsovMA/429.txt
Normal file
0
KuznetsovMA/429.txt
Normal file
1
KuznetsovYuM/428.md
Normal file
1
KuznetsovYuM/428.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428
|
||||||
0
LarikovaAA/428b.md
Normal file
0
LarikovaAA/428b.md
Normal file
0
LukovnikovDE/428.md
Normal file
0
LukovnikovDE/428.md
Normal file
1
MalkinMV/428b.md
Normal file
1
MalkinMV/428b.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b
|
||||||
0
MarkinAM/428b.md
Normal file
0
MarkinAM/428b.md
Normal file
0
MashinDD/429.txt
Normal file
0
MashinDD/429.txt
Normal file
1
MininaVD/427.txt
Normal file
1
MininaVD/427.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
427.txt
|
||||||
1
MininaVD/MininaVD
Normal file
1
MininaVD/MininaVD
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
427.txt
|
||||||
0
MochalovAE/426.txt
Normal file
0
MochalovAE/426.txt
Normal file
0
MusinAA/428b.md
Normal file
0
MusinAA/428b.md
Normal file
1
MylnikovAS/427.md
Normal file
1
MylnikovAS/427.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
|
|
@ -1,250 +0,0 @@
|
||||||
# LInkedList (Node = List = {'name': 'Имя', 'phone': '123', 'next': None}) имена уникальные (id)
|
|
||||||
|
|
||||||
|
|
||||||
def ll_insert(head, name, phone):
|
|
||||||
|
|
||||||
""" проходит до конца (или сразу добавляет в конец) и возвращает новую голову
|
|
||||||
(если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую
|
|
||||||
голову, если вставка может быть в начало """
|
|
||||||
|
|
||||||
new_node = {'name': name, 'phone': phone, 'next': None}
|
|
||||||
|
|
||||||
# если списка не было
|
|
||||||
if head is None:
|
|
||||||
return new_node
|
|
||||||
|
|
||||||
# # вставка в начало O(1)
|
|
||||||
# new_node = {'name': name, 'phone': phone, 'next': head}
|
|
||||||
# return new_node
|
|
||||||
|
|
||||||
# вставка в конец O(n)
|
|
||||||
current = head
|
|
||||||
while current['next'] is not None:
|
|
||||||
# проверка существования данного идентификатора (обновляем запись)
|
|
||||||
if current['name'] == name:
|
|
||||||
current['phone'] = phone
|
|
||||||
return head
|
|
||||||
current = current['next']
|
|
||||||
|
|
||||||
# проверка на id
|
|
||||||
if current['name'] == name:
|
|
||||||
current['phone'] = phone
|
|
||||||
else: current['next'] = new_node
|
|
||||||
return head
|
|
||||||
|
|
||||||
|
|
||||||
def ll_find(head, name):
|
|
||||||
|
|
||||||
""" ищет узел, возвращает телефон или None """
|
|
||||||
|
|
||||||
current = head
|
|
||||||
while current is not None:
|
|
||||||
if current['name'] == name:
|
|
||||||
return current['phone']
|
|
||||||
current = current['next']
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def ll_delete(head, name):
|
|
||||||
|
|
||||||
""" удаляет узел, возвращает новую голову """
|
|
||||||
|
|
||||||
if head is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Удаление первого
|
|
||||||
if head['name'] == name:
|
|
||||||
new_head = head['next']
|
|
||||||
head['next'] = None
|
|
||||||
return new_head
|
|
||||||
|
|
||||||
# Если не первый
|
|
||||||
current = head
|
|
||||||
while current['next'] is not None:
|
|
||||||
if current['next']['name'] == name:
|
|
||||||
target = current['next']
|
|
||||||
current['next'] = target['next']
|
|
||||||
target['next'] = None
|
|
||||||
return head
|
|
||||||
current = current['next']
|
|
||||||
|
|
||||||
return head
|
|
||||||
|
|
||||||
|
|
||||||
def ll_list_all(head):
|
|
||||||
|
|
||||||
""" собирает все записи в список и сортирует (сортировка вынесена отдельно) """
|
|
||||||
|
|
||||||
length = ll_Lenght(head)
|
|
||||||
new_list = [None]*length
|
|
||||||
current = head
|
|
||||||
for i in range(length):
|
|
||||||
new_list[i] = (current['name'], current['phone'])
|
|
||||||
current = current['next']
|
|
||||||
sorten(new_list)
|
|
||||||
return new_list
|
|
||||||
|
|
||||||
|
|
||||||
# вспомогательные функции--------------------------------
|
|
||||||
def ll_Lenght(head):
|
|
||||||
# длина связного списка
|
|
||||||
counter = 0
|
|
||||||
curr = head
|
|
||||||
while curr:
|
|
||||||
counter += 1
|
|
||||||
curr = curr['next']
|
|
||||||
return counter
|
|
||||||
|
|
||||||
|
|
||||||
def sorten(arr):
|
|
||||||
n = len(arr)
|
|
||||||
for i in range(n):
|
|
||||||
for j in range(0, n - i - 1):
|
|
||||||
if arr[j][0] > arr[j + 1][0]:
|
|
||||||
arr[j], arr[j + 1] = arr[j + 1], arr[j]
|
|
||||||
# -----------------------------------------------------------
|
|
||||||
|
|
||||||
# HashTable (Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None))
|
|
||||||
|
|
||||||
def hash_table(size):
|
|
||||||
return [None]*size
|
|
||||||
|
|
||||||
|
|
||||||
def hash_func(name, buckets_count):
|
|
||||||
h = 0
|
|
||||||
for char in name:
|
|
||||||
h += ord(char)
|
|
||||||
return h % buckets_count
|
|
||||||
|
|
||||||
|
|
||||||
def ht_insert(buckets, name, phone):
|
|
||||||
|
|
||||||
""" вычисляет индекс, вызывает ll_insert для соответствующего бакета """
|
|
||||||
|
|
||||||
if buckets is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
index = hash_func(name, len(buckets))
|
|
||||||
buckets[index] = ll_insert(buckets[index], name, phone)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_find(buckets, name):
|
|
||||||
""" """
|
|
||||||
idx = hash_func(name, len(buckets))
|
|
||||||
return ll_find(buckets[idx], name)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_delete(buckets, name):
|
|
||||||
idx = hash_func(name, len(buckets))
|
|
||||||
buckets[idx] = ll_delete(buckets[idx], name)
|
|
||||||
|
|
||||||
|
|
||||||
def ht_list_all(buckets):
|
|
||||||
|
|
||||||
""" собирает все записи из всех бакетов и сортирует """
|
|
||||||
total_count = 0
|
|
||||||
for head in buckets:
|
|
||||||
total_count += ll_Lenght(head)
|
|
||||||
|
|
||||||
full_data = [None]*total_count
|
|
||||||
|
|
||||||
k = 0
|
|
||||||
for head in buckets:
|
|
||||||
curr = head
|
|
||||||
while curr:
|
|
||||||
full_data[k] = (curr['name'], curr['phone'])
|
|
||||||
k += 1
|
|
||||||
curr = curr['next']
|
|
||||||
|
|
||||||
sorten(full_data)
|
|
||||||
return full_data
|
|
||||||
|
|
||||||
|
|
||||||
# Двоичное дерево поиска : Узел — словарь: {'name': 'Имя', 'phone': '123', 'left': None, 'right': None}
|
|
||||||
|
|
||||||
def bst_insert(root, name, phone):
|
|
||||||
|
|
||||||
""" рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется) """
|
|
||||||
|
|
||||||
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
|
|
||||||
|
|
||||||
# если дерева нет
|
|
||||||
if root is None:
|
|
||||||
return new_node
|
|
||||||
|
|
||||||
if name < root['name']:
|
|
||||||
root['left'] = bst_insert(root['left'], name, phone)
|
|
||||||
elif name > root['name']:
|
|
||||||
root['right'] = bst_insert(root['right'], name, phone)
|
|
||||||
else:
|
|
||||||
root['phone'] = phone
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
def bst_find(root, name):
|
|
||||||
|
|
||||||
""" поиск """
|
|
||||||
|
|
||||||
if root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if root['name'] == name:
|
|
||||||
return root['phone']
|
|
||||||
|
|
||||||
elif name < root['name']:
|
|
||||||
return bst_find(root['left'], name)
|
|
||||||
|
|
||||||
elif name > root['name']:
|
|
||||||
return bst_find(root['right'], name)
|
|
||||||
|
|
||||||
|
|
||||||
def bst_delete(root, name):
|
|
||||||
|
|
||||||
""" удаление, возвращает новый корень """
|
|
||||||
|
|
||||||
if root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# спускаемся к нужному узлу (аналогично поиску)
|
|
||||||
elif name < root['name']:
|
|
||||||
root['left'] = bst_delete(root['left'], name)
|
|
||||||
|
|
||||||
elif name > root['name']:
|
|
||||||
root['right'] = bst_delete(root['right'], name)
|
|
||||||
|
|
||||||
# стоим в нужном узле
|
|
||||||
else:
|
|
||||||
# узла слева нет (вернет правого ребенка или None)
|
|
||||||
if root['left'] is None:
|
|
||||||
return root['right']
|
|
||||||
|
|
||||||
# узла справа нет (вернет левого ребенка)
|
|
||||||
if root['right'] is None:
|
|
||||||
return root['left']
|
|
||||||
|
|
||||||
# два наследника (поиск минимального поддерева в правой ветке)
|
|
||||||
|
|
||||||
successor = root['right']
|
|
||||||
while successor['left'] is not None:
|
|
||||||
successor = successor['left']
|
|
||||||
|
|
||||||
root['name'] = successor['name']
|
|
||||||
root['phone'] = successor['phone']
|
|
||||||
# Удаляем дубликат преемника в правом поддереве
|
|
||||||
root['right'] = bst_delete(root['right'], successor['name'])
|
|
||||||
|
|
||||||
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
def bst_list_all(root, result=None):
|
|
||||||
""" центрированный обход (рекурсивно собирает записи в отсортированном порядке) """
|
|
||||||
if result is None:
|
|
||||||
result = []
|
|
||||||
# сначала спускаемся по левой стороне вниз, затем идем вверх и вправо
|
|
||||||
if root is not None:
|
|
||||||
bst_list_all(root['left'], result)
|
|
||||||
result.append((root['name'], root['phone']))
|
|
||||||
bst_list_all(root['right'], result)
|
|
||||||
return result
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import pandas as pd
|
|
||||||
import glob
|
|
||||||
|
|
||||||
folder_path = 'results'
|
|
||||||
|
|
||||||
sizes = ['500', '1000', '2000', '5000', '10000']
|
|
||||||
|
|
||||||
|
|
||||||
for size in sizes:
|
|
||||||
files = glob.glob(f'{folder_path}/timedata_{size}_epochs_*.csv')
|
|
||||||
|
|
||||||
data = [pd.read_csv(f)['Время (сек)'] for f in files]
|
|
||||||
|
|
||||||
datatomean = pd.concat(data, axis=1)
|
|
||||||
datamean = datatomean.mean(axis=1)
|
|
||||||
|
|
||||||
df = pd.read_csv(files[0])
|
|
||||||
df['Время (сек)'] = datamean
|
|
||||||
|
|
||||||
df.to_csv(f'results/aaverage_timedata_{size}.csv', index=False)
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
import pandas as pd
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
from matplotlib.ticker import AutoMinorLocator
|
|
||||||
|
|
||||||
df500 = pd.read_csv("results/aaverage_timedata_500.csv")
|
|
||||||
df1000 = pd.read_csv("results/aaverage_timedata_1000.csv")
|
|
||||||
df2000 = pd.read_csv("results/aaverage_timedata_2000.csv")
|
|
||||||
df5000 = pd.read_csv("results/aaverage_timedata_5000.csv")
|
|
||||||
df10000 = pd.read_csv("results/aaverage_timedata_10000.csv")
|
|
||||||
|
|
||||||
|
|
||||||
def select_data_list(ax):
|
|
||||||
dfs = [df500, df1000, df2000, df5000, df10000]
|
|
||||||
Nvals = [500, 1000, 2000, 5000, 10000]
|
|
||||||
# delete, find, insert
|
|
||||||
# список:
|
|
||||||
valsSort = [list(arr[(arr['Структура'] == "linklist") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
|
|
||||||
valsShuff = [list(arr[(arr['Структура'] == "linklist") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
|
|
||||||
# 0 - sorted 1 - shuffled
|
|
||||||
# delete
|
|
||||||
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
|
|
||||||
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
|
|
||||||
# find
|
|
||||||
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
|
|
||||||
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
|
|
||||||
# insert
|
|
||||||
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
|
|
||||||
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
|
|
||||||
|
|
||||||
|
|
||||||
def select_data_hasht(ax):
|
|
||||||
dfs = [df500, df1000, df2000, df5000, df10000]
|
|
||||||
Nvals = [500, 1000, 2000, 5000, 10000]
|
|
||||||
# delete, find, insert
|
|
||||||
# список:
|
|
||||||
valsSort = [list(arr[(arr['Структура'] == "hashtable") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
|
|
||||||
valsShuff = [list(arr[(arr['Структура'] == "hashtable") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
|
|
||||||
# 0 - sorted 1 - shuffled
|
|
||||||
# delete
|
|
||||||
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
|
|
||||||
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
|
|
||||||
# find
|
|
||||||
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
|
|
||||||
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
|
|
||||||
# insert
|
|
||||||
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
|
|
||||||
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
|
|
||||||
|
|
||||||
|
|
||||||
def select_data_tree(ax):
|
|
||||||
dfs = [df500, df1000, df2000, df5000, df10000]
|
|
||||||
Nvals = [500, 1000, 2000, 5000, 10000]
|
|
||||||
# delete, find, insert
|
|
||||||
# список:
|
|
||||||
valsSort = [list(arr[(arr['Структура'] == "bintree") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
|
|
||||||
valsShuff = [list(arr[(arr['Структура'] == "bintree") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
|
|
||||||
# 0 - sorted 1 - shuffled
|
|
||||||
# delete
|
|
||||||
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
|
|
||||||
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
|
|
||||||
# find
|
|
||||||
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
|
|
||||||
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
|
|
||||||
# insert
|
|
||||||
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
|
|
||||||
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
|
|
||||||
|
|
||||||
# построение графика
|
|
||||||
def design_show_graph(title, version, ymaxlim):
|
|
||||||
fig, ax = plt.subplots(figsize=(10, 5), nrows=1, ncols=2)
|
|
||||||
for i in range(2):
|
|
||||||
match title:
|
|
||||||
case "Tree":
|
|
||||||
select_data_tree(ax)
|
|
||||||
case "Linklist":
|
|
||||||
select_data_list(ax)
|
|
||||||
case "hasht":
|
|
||||||
select_data_hasht(ax)
|
|
||||||
ax[0].set_title(f"График сложностей для {title} (sort)")
|
|
||||||
ax[1].set_title(f"График сложностей для {title} (shuff)")
|
|
||||||
ax[i].set_xlabel("N")
|
|
||||||
ax[i].set_ylabel("сек * ")
|
|
||||||
ax[i].grid(which="major", linewidth=1.5)
|
|
||||||
ax[i].grid(which="minor", color="gray", linewidth=0.5)
|
|
||||||
ax[i].xaxis.set_minor_locator(AutoMinorLocator())
|
|
||||||
ax[i].yaxis.set_minor_locator(AutoMinorLocator())
|
|
||||||
ax[i].legend()
|
|
||||||
ax[i].set_ylim(0, ymaxlim)
|
|
||||||
plt.savefig(f'graphics\{title}{version}.png', dpi=200)
|
|
||||||
plt.savefig(f'graphics\T{title}{version}.eps', dpi=200)
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
design_show_graph("hasht", 2, 0.4)
|
|
||||||
Binary file not shown.
|
|
@ -1,136 +0,0 @@
|
||||||
\input{preambule.tex}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\begin{document}
|
|
||||||
|
|
||||||
% --- ТИТУЛЬНЫЙ ЛИСТ (упрощенно) ---
|
|
||||||
\begin{titlepage}
|
|
||||||
\centering
|
|
||||||
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РФ \\
|
|
||||||
«Национальный исследовательский Нижегородский государственный университет им. Н.И. Лобачевского» \\
|
|
||||||
\vspace{4cm}
|
|
||||||
\Large ОТЧЕТ К ЛАБОРАТОРНОЙ РАБОТЕ \\
|
|
||||||
\vspace{1cm}
|
|
||||||
\large «Реализация и экспериментальное сравнение базовых структур данных на примере телефонного справочника» \\
|
|
||||||
\vspace{4cm}
|
|
||||||
\flushright
|
|
||||||
Выполнил: студент В. В. Пронин \\
|
|
||||||
Преподаватель: Н. С. Морозов \\
|
|
||||||
\vfill
|
|
||||||
Нижний Новгород \\
|
|
||||||
2024
|
|
||||||
\end{titlepage}
|
|
||||||
|
|
||||||
\newpage
|
|
||||||
\tableofcontents
|
|
||||||
\newpage
|
|
||||||
|
|
||||||
\section{Введение}
|
|
||||||
|
|
||||||
Эффективность программных систем во многом определяется выбором способов организации данных в оперативной памяти. Задача разработки телефонного справочника является классическим примером, требующим баланса между скоростью вставки новых записей, поиском по ключу и эффективным удалением.
|
|
||||||
|
|
||||||
В рамках данной работы исследуются три фундаментальные структуры данных, реализованные «с нуля» в процедурной парадигме программирования на языке Python:
|
|
||||||
\begin{itemize}
|
|
||||||
\item \textbf{Связный список (Linked List)} --- динамическая структура, позволяющая оценить базовые механизмы управления указателями и демонстрирующая линейную сложность операций $O(n)$.
|
|
||||||
\item \textbf{Хеш-таблица (Hash Table)} --- ассоциативный массив, использующий хеширование для обеспечения прямого доступа к данным. Реализация позволяет изучить методы разрешения коллизий и преимущества константной сложности $O(1)$.
|
|
||||||
\item \textbf{Двоичное дерево поиска (BST)} --- иерархическая структура, обеспечивающая логарифмическую скорость доступа $O(\log n)$ и поддерживающая упорядоченность данных «из коробки».
|
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
\textbf{Цель работы:} Изучить внутренние алгоритмы работы перечисленных структур, реализовать их без использования встроенных высокоуровневых контейнеров и экспериментально подтвердить теоретические оценки временной сложности на случайных и отсортированных наборах данных.
|
|
||||||
|
|
||||||
\section{Реализация структур данных}
|
|
||||||
\subsection{Связный список}
|
|
||||||
% Здесь опишите логику ll_insert, ll_find и ll_delete
|
|
||||||
\subsection{Хеш-таблица}
|
|
||||||
% Опишите хеш-функцию и метод цепочек
|
|
||||||
\subsection{Двоичное дерево поиска}
|
|
||||||
% Опишите рекурсивные алгоритмы и проблему деградации
|
|
||||||
|
|
||||||
\section{Методика эксперимента}
|
|
||||||
Замеры производились для наборов данных объемом $N=500, 1000, 2000, 5000, 10000$ элементов. Использовались два сценария: перемешанные (\textit{shuffled}) и отсортированные по алфавиту (\textit{sorted}) записи. Каждая операция выполнялась 5 раз с последующим вычислением среднего арифметического значения с помощью функции \texttt{time.perf\_counter()}.
|
|
||||||
|
|
||||||
\section{Результаты и анализ}
|
|
||||||
Было проведено серию опытов для $N$ от 500 до 10000.
|
|
||||||
\subsection*{1. Бинарное дерево поиска (BST) и влияние порядка}
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/TTree1.eps}
|
|
||||||
\caption{Зависимость времени выполнения операций в BST от объема данных}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/TTree2.eps}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item \textbf{Деградация на отсортированных данных:} При вставке отсортированных данных время увеличилось с \textbf{0.124с} ($N=1000$) до \textbf{13.27с} ($N=10000$). Рост времени в 100 раз при увеличении объема данных в 10 раз четко указывает на квадратичную сложность $O(n^2)$ для процесса заполнения всей структуры. Дерево выродилось в линейный список, и поиск места вставки стал занимать $O(n)$ вместо ожидаемого $O(\log n)$.
|
|
||||||
\item \textbf{Эффективность на перемешанных данных:} На \texttt{shuffled} данных вставка 10000 элементов заняла всего \textbf{0.031с}. Это подтверждает логарифмическую сложность $O(\log n)$ для операций в дереве при случайном распределении ключей.
|
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
\subsection*{2. Хеш-таблица: Стабильность и скорость}
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/Thasht1.eps}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/Thasht2.eps}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item \textbf{Чувствительность к порядку:} Хеш-таблица показала идентичные результаты как на \texttt{shuffled}, так и на \texttt{sorted} данных (около \textbf{0.165с} -- \textbf{0.167с} для 10000 вставок). Это объясняется тем, что хеш-функция распределяет ключи по бакетам независимо от их исходного порядка, предотвращая деградацию структуры.
|
|
||||||
\item \textbf{Превосходство:} На больших объемах хеш-таблица оказалась самой быстрой структурой для поиска и удаления ($\approx 0.001$с при $N=10000$), что подтверждает теоретическую среднюю сложность $O(1)$.
|
|
||||||
\item \textbf{Замечание:} Так как реализация использует списки для разрешения коллизий со вставкой в конец, при заполнении таблицы наблюдается рост времени вставки, стремящийся к квадратичному, однако абсолютные значения остаются на порядки ниже, чем у выродившегося BST.
|
|
||||||
\end{itemize}
|
|
||||||
\subsection*{3. Связный список: Линейная зависимость}
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/Tlinklist1.eps}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{figure}[H]
|
|
||||||
\centering
|
|
||||||
\includegraphics[scale=0.7]{plots/Tlinklist2.eps}
|
|
||||||
\end{figure}
|
|
||||||
|
|
||||||
\begin{itemize}
|
|
||||||
\item \textbf{Поиск и удаление:} Связный список показал худшие результаты среди всех структур на случайных данных. Время поиска при 10000 элементах (\textbf{0.029с}) значительно медленнее, чем у BST на перемешанных данных (\textbf{0.0002с}). Это подтверждает линейную сложность $O(n)$.
|
|
||||||
\item \textbf{Вставка:} Вставка (вероятно, в конец или с сохранением порядка) дает $O(n^2)$ при заполнении (\textbf{2.83с} -- \textbf{3.00с} на 10000 эл.). Характер роста времени при переходе от $N=5000$ (\textbf{0.71с}) к $N=10000$ подтверждает квадратичную зависимость.
|
|
||||||
\end{itemize}
|
|
||||||
|
|
||||||
\subsection*{Вывод: выбор структуры данных}
|
|
||||||
\begin{enumerate}
|
|
||||||
\item \textbf{Хеш-таблица} — наиболее универсальный выбор. Она обеспечивает стабильное $O(1)$ для поиска и не зависит от порядка входящих данных.
|
|
||||||
\item \textbf{BST} — крайне эффективен ($O(\log n)$) при случайном распределении данных, но без механизмов самобалансировки критически уязвим к отсортированным входным последовательностям, замедляясь до уровня списка.
|
|
||||||
\item \textbf{Связный список} — продемонстрировал самую низкую производительность на операциях поиска и массовой вставки. Его использование оправдано только в специфических сценариях (например, реализация стека), где работа ведется исключительно с головой списка за $O(1)$.
|
|
||||||
\end{enumerate}
|
|
||||||
|
|
||||||
|
|
||||||
\subsection*{Сводная таблица результатов}
|
|
||||||
\begin{table}[H]
|
|
||||||
\centering
|
|
||||||
\small
|
|
||||||
\begin{tabular}{|l|l|c|c|c|c|c|}
|
|
||||||
\hline
|
|
||||||
\textbf{Структура} & \textbf{Режим} & \textbf{Опер.} & \textbf{N=500} & \textbf{N=1000} & \textbf{N=5000} & \textbf{N=10000} \\ \hline
|
|
||||||
\multirow{3}{*}{LinkList} & Shuffled & Insert & 0.0066 & 0.0292 & 0.7089 & 2.8358 \\
|
|
||||||
& Shuffled & Find & 0.0012 & 0.0026 & 0.0147 & 0.0289 \\
|
|
||||||
& Sorted & Insert & 0.0065 & 0.0290 & 0.7637 & 3.0042 \\ \hline
|
|
||||||
\multirow{3}{*}{HashTable} & Shuffled & Insert & 0.0007 & 0.0022 & 0.0468 & 0.1670 \\
|
|
||||||
& Shuffled & Find & 0.0001 & 0.0002 & 0.0008 & 0.0014 \\
|
|
||||||
& Sorted & Insert & 0.0007 & 0.0022 & 0.0448 & 0.1646 \\ \hline
|
|
||||||
\multirow{3}{*}{BinTree} & Shuffled & Insert & 0.0009 & 0.0021 & 0.0145 & 0.0309 \\
|
|
||||||
& Shuffled & Find & 0.0001 & 0.0001 & 0.0002 & 0.0002 \\
|
|
||||||
& Sorted & Insert & \textbf{0.0298} & \textbf{0.1239} & \textbf{3.3052} & \textbf{13.2706} \\ \hline
|
|
||||||
\end{tabular}
|
|
||||||
\caption{Сравнение минимального времени выполнения операций (в секундах) в зависимости от объема данных $N$}
|
|
||||||
\end{table}
|
|
||||||
|
|
||||||
|
|
||||||
\end{document}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,46 +0,0 @@
|
||||||
%\documentclass[a4paper, 12pt]{article}
|
|
||||||
\documentclass[a4paper, 14pt]{extarticle}
|
|
||||||
|
|
||||||
\usepackage[english, russian]{babel}
|
|
||||||
\usepackage[T2A]{fontenc}
|
|
||||||
\usepackage[utf8]{inputenc}
|
|
||||||
\usepackage{comment}
|
|
||||||
|
|
||||||
\usepackage{multirow}
|
|
||||||
\usepackage{fontspec}
|
|
||||||
\setmainfont{Times New Roman}
|
|
||||||
|
|
||||||
\usepackage{amsmath}
|
|
||||||
\usepackage{amssymb}
|
|
||||||
|
|
||||||
\usepackage{geometry}
|
|
||||||
\usepackage{titleps}
|
|
||||||
\usepackage{graphicx}
|
|
||||||
\DeclareGraphicsExtensions{.pdf, .jpg}
|
|
||||||
\usepackage{wrapfig}
|
|
||||||
|
|
||||||
|
|
||||||
\usepackage{indentfirst}
|
|
||||||
|
|
||||||
|
|
||||||
\geometry{top=20mm}
|
|
||||||
\geometry{bottom=25mm}
|
|
||||||
\geometry{left=30mm}
|
|
||||||
\geometry{right=10mm}
|
|
||||||
|
|
||||||
\usepackage{float}
|
|
||||||
\usepackage{wrapfig}
|
|
||||||
|
|
||||||
\newpagestyle{main}{
|
|
||||||
\setheadrule{0.4pt}
|
|
||||||
\sethead{ННГУ им Н.И. Лобачесвкого}{}{В. В. Пронин}
|
|
||||||
|
|
||||||
\setfoot{}{\thepage}{}
|
|
||||||
}
|
|
||||||
\pagestyle{main}
|
|
||||||
%\setcounter{page}{2}
|
|
||||||
|
|
||||||
\linespread{1.5}
|
|
||||||
\setlength{\parindent}{10mm}
|
|
||||||
\setlength{\parskip}{1ex}
|
|
||||||
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
Структура,Режим,Операция,Время (сек)
|
|
||||||
linklist,shuffled,insert,0.02917951999970676
|
|
||||||
linklist,shuffled,find,0.00256621999997146
|
|
||||||
linklist,shuffled,delete,0.0018302000000403
|
|
||||||
hashtable,shuffled,insert,0.00223439999972464
|
|
||||||
hashtable,shuffled,find,0.00018408000032643998
|
|
||||||
hashtable,shuffled,delete,0.00013254000023147998
|
|
||||||
bintree,shuffled,insert,0.00211651999998134
|
|
||||||
bintree,shuffled,find,0.00014015999986434
|
|
||||||
bintree,shuffled,delete,7.299999997485429e-05
|
|
||||||
linklist,sorted,insert,0.02902601999994654
|
|
||||||
linklist,sorted,find,0.00272362000014248
|
|
||||||
linklist,sorted,delete,0.0017690399998172598
|
|
||||||
hashtable,sorted,insert,0.00219620000007122
|
|
||||||
hashtable,sorted,find,0.00018717999992074
|
|
||||||
hashtable,sorted,delete,0.00011756000003512
|
|
||||||
bintree,sorted,insert,0.12391134000008604
|
|
||||||
bintree,sorted,find,0.0079993400002422
|
|
||||||
bintree,sorted,delete,0.004170019999764881
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
Структура,Режим,Операция,Время (сек)
|
|
||||||
linklist,shuffled,insert,2.835846880000099
|
|
||||||
linklist,shuffled,find,0.02894071999999136
|
|
||||||
linklist,shuffled,delete,0.017179720000240158
|
|
||||||
hashtable,shuffled,insert,0.16700972000016914
|
|
||||||
hashtable,shuffled,find,0.0014067599999179402
|
|
||||||
hashtable,shuffled,delete,0.00103095999966166
|
|
||||||
bintree,shuffled,insert,0.030944720000115878
|
|
||||||
bintree,shuffled,find,0.00019450000017964003
|
|
||||||
bintree,shuffled,delete,9.787999988471869e-05
|
|
||||||
linklist,sorted,insert,3.0041990600000643
|
|
||||||
linklist,sorted,find,0.02895102000002222
|
|
||||||
linklist,sorted,delete,0.016321099999913664
|
|
||||||
hashtable,sorted,insert,0.16461017999990868
|
|
||||||
hashtable,sorted,find,0.0014511600000332201
|
|
||||||
hashtable,sorted,delete,0.0010335000002669001
|
|
||||||
bintree,sorted,insert,13.270635900000162
|
|
||||||
bintree,sorted,find,0.08588061999998894
|
|
||||||
bintree,sorted,delete,0.04398507999994758
|
|
||||||
|
|
|
@ -1,140 +0,0 @@
|
||||||
from aufg1 import *
|
|
||||||
import time
|
|
||||||
import random
|
|
||||||
import sys
|
|
||||||
import csv
|
|
||||||
|
|
||||||
sys.setrecursionlimit(20000)
|
|
||||||
|
|
||||||
def phone_number_generate():
|
|
||||||
number = "8"
|
|
||||||
text = "0123456789"
|
|
||||||
for i in range(10):
|
|
||||||
char = random.choice(text)
|
|
||||||
number += char
|
|
||||||
return number
|
|
||||||
|
|
||||||
def create_data(n=100):
|
|
||||||
|
|
||||||
""" создаем сразу обычный массив и остортированный """
|
|
||||||
|
|
||||||
records_sorted = []
|
|
||||||
for i in range(n):
|
|
||||||
name = f"User_{i:05d}"
|
|
||||||
phone = phone_number_generate()
|
|
||||||
records_sorted.append((name, phone))
|
|
||||||
|
|
||||||
records_shuffled = records_sorted[:]
|
|
||||||
random.shuffle(records_shuffled)
|
|
||||||
return records_sorted, records_shuffled
|
|
||||||
|
|
||||||
|
|
||||||
def run_expirement(epoch=1, elements=1000):
|
|
||||||
|
|
||||||
""" распределяем данные по трем структурам данных
|
|
||||||
тестируем время операций (вставки, удаления, перебора) и записываем полученные результаты в файл """
|
|
||||||
header = ["Структура", "Режим", "Операция", "Время (сек)"]
|
|
||||||
|
|
||||||
for j in range(epoch):
|
|
||||||
print(f"эпоха - {j+1}")
|
|
||||||
|
|
||||||
results = [header]
|
|
||||||
# создаем данные
|
|
||||||
records_sorted, records_shuffled = create_data(elements)
|
|
||||||
|
|
||||||
datasets = [
|
|
||||||
("shuffled", records_shuffled),
|
|
||||||
("sorted", records_sorted)]
|
|
||||||
|
|
||||||
# сразу будем обрабатывать и случайны и отсортированный данные
|
|
||||||
for label, arr in datasets:
|
|
||||||
|
|
||||||
linklist = None
|
|
||||||
hashtab = hash_table(elements)
|
|
||||||
bintree = None
|
|
||||||
# заполнение связного списка
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in arr:
|
|
||||||
linklist = ll_insert(linklist, p[0], p[1])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["linklist", label, "insert", end-start])
|
|
||||||
|
|
||||||
# поиск 110 имен в связном списке
|
|
||||||
# несуществующие данные
|
|
||||||
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
|
|
||||||
# случайная комбинация
|
|
||||||
chaossample = random.sample(arr, 100) + nonedata
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in chaossample:
|
|
||||||
ll_find(linklist, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["linklist", label, "find", end-start])
|
|
||||||
|
|
||||||
# удаление 50 имен в св писке
|
|
||||||
deldata = random.sample(arr, 50)
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in deldata:
|
|
||||||
ll_delete(linklist, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["linklist", label, "delete", end-start])
|
|
||||||
|
|
||||||
# заполнение хэш-тфблицы
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in arr:
|
|
||||||
ht_insert(hashtab, p[0], p[1])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["hashtable", label, "insert", end-start])
|
|
||||||
|
|
||||||
# поиск 110 имен в хэш таблице
|
|
||||||
# несуществующие данные
|
|
||||||
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
|
|
||||||
# случайная комбинация
|
|
||||||
chaossample = random.sample(arr, 100) + nonedata
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in chaossample:
|
|
||||||
ht_find(hashtab, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["hashtable", label, "find", end-start])
|
|
||||||
|
|
||||||
# удаление 50 имен в хэш таблице
|
|
||||||
deldata = random.sample(arr, 50)
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in deldata:
|
|
||||||
ht_delete(hashtab, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["hashtable", label, "delete", end-start])
|
|
||||||
|
|
||||||
# заполнение дерева
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in arr:
|
|
||||||
bintree = bst_insert(bintree, p[0], p[1])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["bintree", label, "insert", end-start])
|
|
||||||
|
|
||||||
# поиск 110 имен в дереве
|
|
||||||
# несуществующие данные
|
|
||||||
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
|
|
||||||
# случайная комбинация
|
|
||||||
chaossample = random.sample(arr, 100) + nonedata
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in chaossample:
|
|
||||||
bst_find(bintree, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["bintree", label, "find", end-start])
|
|
||||||
|
|
||||||
# удаление 50 имен в дереве
|
|
||||||
deldata = random.sample(arr, 50)
|
|
||||||
start = time.perf_counter()
|
|
||||||
for p in deldata:
|
|
||||||
bst_delete(bintree, p[0])
|
|
||||||
end = time.perf_counter()
|
|
||||||
results.append(["bintree", label, "delete", end-start])
|
|
||||||
|
|
||||||
filename = f"results/timedata_{elements}_epochs_{j+1}.csv"
|
|
||||||
with open(filename, mode='w', encoding='utf-8', newline='') as file:
|
|
||||||
writer = csv.writer(file)
|
|
||||||
writer.writerows(results)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
run_expirement(epoch=5, elements=5000)
|
|
||||||
336
README.md
336
README.md
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
### Крайний срок приема работ 25.05.2026 до 14:00
|
### Крайний срок приема работ 25.05.2026 до 14:00
|
||||||
|
|
||||||
## Задание 1 -- репозиторий
|
## Задание 0 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026]
|
||||||
|
|
||||||
0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе).
|
0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе).
|
||||||
|
|
||||||
|
|
@ -38,17 +38,345 @@
|
||||||
5. **Сохрани изменения:**
|
5. **Сохрани изменения:**
|
||||||
```bash
|
```bash
|
||||||
git add -A
|
git add -A
|
||||||
git commit -m "Добавлен файл группы [номер] для [Фамилия]"
|
git commit -m "[0] initial commit"
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Отправь ветку **в свой форк** на Gitea:
|
6. Отправь ветку **в свой форк** на Gitea:
|
||||||
```bash
|
```bash
|
||||||
git push origin IvanovII
|
git push origin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
если просит, перед этим сделать git push --set-upstream origin
|
||||||
|
|
||||||
7. **Создай запрос на слияние (Pull Request):** На Gitea перейди в свой форк, выбери ветку `IvanovII`, нажмите **Запрос на слияние**. Убедитесь, что:
|
7. **Создай запрос на слияние (Pull Request):** На Gitea перейди в свой форк, выбери ветку `IvanovII`, нажмите **Запрос на слияние**. Убедитесь, что:
|
||||||
- Базовый репозиторий: **учебный** (преподавателя)
|
- Базовый репозиторий: **учебный** (преподавателя)
|
||||||
- Базовая ветка: **develop**
|
- Базовая ветка: **develop**
|
||||||
- Сравниваемая ветка: **свой форк / IvanovII**
|
- Сравниваемая ветка: **свой форк / IvanovII**
|
||||||
|
|
||||||
8. Отправь PR.
|
8. Отправь PR.
|
||||||
|
|
||||||
|
## Задание 1 -- структуры данных
|
||||||
|
***Напоминание: под каждое задание вы создаете отдельную ветку***
|
||||||
|
|
||||||
|
>Для оформления результатов заведи папку **docs** в своей папке и сохраняй туда отчет (в любом формате от .doc до .md, а то и .jpnb). Вспомогательные файлы клади в подпапку **data** внутри **docs**
|
||||||
|
|
||||||
|
**Цель работы**
|
||||||
|
|
||||||
|
Реализовать три различные структуры данных «с нуля», применить их для хранения записей телефонного справочника и экспериментально сравнить производительность основных операций. Вы должны собственными руками написать код, чтобы понять внутреннее устройство связного списка, хеш-таблицы и двоичного дерева поиска, а также осознать их сильные и слабые стороны на практике.
|
||||||
|
|
||||||
|
**!! Задание выполнять в структурной (процедурной) парадигме, не используя классы. Главное реализовать структуры данных «руками» и сравнить их производительность.**
|
||||||
|
|
||||||
|
### Базовые операции (обязательны для всех):
|
||||||
|
|
||||||
|
`insert(name, phone)` -- добавить или обновить запись.
|
||||||
|
|
||||||
|
`find(name)` -- phone или None.
|
||||||
|
|
||||||
|
`delete(name)` -- удалить запись, игнорировать отсутствие.
|
||||||
|
|
||||||
|
`list_all()` -- список всех записей, отсортированный по имени (для BST in‑order обход; для списка и хеш‑таблицы — собрать и отсортировать явно).
|
||||||
|
|
||||||
|
#### 1. Связный список (LinkedListPhoneBook)
|
||||||
|
|
||||||
|
Узел представляется словарём: `{'name': 'Имя', 'phone': '123', 'next': None}.`
|
||||||
|
|
||||||
|
**Функции:**
|
||||||
|
|
||||||
|
`def ll_insert(head, name, phone)` — проходит до конца (или сразу добавляет в конец) и возвращает новую голову (если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую голову, если вставка может быть в начало.
|
||||||
|
|
||||||
|
`def ll_find(head, name)` — ищет узел, возвращает телефон или None.
|
||||||
|
|
||||||
|
`def ll_delete(head, name)` — удаляет узел, возвращает новую голову.
|
||||||
|
|
||||||
|
`def ll_list_all(head)` — собирает все записи в список и сортирует (сортировка вынесена отдельно).
|
||||||
|
|
||||||
|
#### 2. Хеш-таблица
|
||||||
|
Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None).
|
||||||
|
|
||||||
|
**Функции:**
|
||||||
|
|
||||||
|
`def ht_insert(buckets, name, phone)` — вычисляет индекс, вызывает ll_insert для соответствующего бакета.
|
||||||
|
|
||||||
|
Аналогично `ht_find, ht_delete, ht_list_all` (последняя собирает все записи из всех бакетов и сортирует).
|
||||||
|
|
||||||
|
#### 3. Двоичное дерево поиска
|
||||||
|
Узел — словарь: `{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.`
|
||||||
|
|
||||||
|
**Функции:**
|
||||||
|
|
||||||
|
`def bst_insert(root, name, phone)` — рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется).
|
||||||
|
|
||||||
|
`def bst_find(root, name)` — поиск.
|
||||||
|
|
||||||
|
`def bst_delete(root, name)` — удаление, возвращает новый корень.
|
||||||
|
|
||||||
|
`def bst_list_all(root)` — центрированный обход (рекурсивно собирает записи в отсортированном порядке).
|
||||||
|
|
||||||
|
### Экспериментальная часть (подробно об измерении времени)
|
||||||
|
#### 1. Генерация тестовых данных
|
||||||
|
Создайте список records из N элементов (например, N = 10000). Каждый элемент — кортеж (name, phone).
|
||||||
|
|
||||||
|
Имена генерируйте как `f"User_{i:05d}"` (равномерное распределение) или случайные слова из небольшого набора (чтобы были повторения и коллизии). Для проверки влияния порядка подготовьте два варианта одного и того же набора:
|
||||||
|
|
||||||
|
`records_shuffled` — случайный порядок.
|
||||||
|
|
||||||
|
`records_sorted` — отсортированный по имени (по алфавиту).
|
||||||
|
|
||||||
|
#### 2. Инструменты замера времени
|
||||||
|
Используйте модуль **time**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import time
|
||||||
|
|
||||||
|
start = time.perf_counter()
|
||||||
|
# ... операции ...
|
||||||
|
end = time.perf_counter()
|
||||||
|
elapsed = end - start # время в секундах
|
||||||
|
```
|
||||||
|
|
||||||
|
Для многократных замеров удобен `timeit`, но в этой задаче достаточно просто обернуть код в цикл и усреднить.
|
||||||
|
|
||||||
|
#### 3. Проведение замеров
|
||||||
|
Для каждой структуры данных и для каждого режима входных данных (случайный / отсортированный) выполните:
|
||||||
|
|
||||||
|
- А. Вставка всех записей
|
||||||
|
|
||||||
|
Создайте пустую структуру.
|
||||||
|
|
||||||
|
Засеките время, выполните insert для каждой записи из входного списка.
|
||||||
|
|
||||||
|
Зафиксируйте общее время вставки.
|
||||||
|
|
||||||
|
- Б. Поиск 100 случайных записей
|
||||||
|
|
||||||
|
Возьмите 100 случайных имён из того же набора (гарантированно существующих) и 10 имён, которых нет (например, "None_{i}").
|
||||||
|
|
||||||
|
Засеките время на выполнение всех 110 вызовов find.
|
||||||
|
|
||||||
|
- В. Удаление 50 случайных записей
|
||||||
|
|
||||||
|
Выберите 50 случайных имён из набора.
|
||||||
|
|
||||||
|
Засеките время на выполнение delete для каждого.
|
||||||
|
|
||||||
|
|
||||||
|
**!! Важно: после вставки структура остаётся заполненной, поиск и удаление выполняются на ней же. Если нужно повторить замер для другого порядка данных — создавайте новую структуру и заполняйте заново.**
|
||||||
|
|
||||||
|
#### 4. Сохранение результатов
|
||||||
|
|
||||||
|
**!! Каждый эксперимент повторить минимум 5 раз и записывать и среднее время, и все замеры.**
|
||||||
|
|
||||||
|
Соберите все замеры в словарь или список, затем сохраните в CSV-файл:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import csv
|
||||||
|
|
||||||
|
results = [
|
||||||
|
["Структура", "Режим", "Операция", "Время (сек)"],
|
||||||
|
["LinkedList", "случайный", "вставка", 0.123],
|
||||||
|
...
|
||||||
|
]
|
||||||
|
|
||||||
|
with open("results.csv", "w", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(results)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### 5. Анализ результатов
|
||||||
|
Постройте график (столбчатая диаграмма или линейный график) — можно в Excel, Google Sheets или с помощью matplotlib в Python.
|
||||||
|
|
||||||
|
Сравните:
|
||||||
|
|
||||||
|
- Как порядок входных данных влияет на скорость вставки в BST (деградация до O(n) на отсортированных данных).
|
||||||
|
|
||||||
|
- Почему хеш-таблица почти не чувствительна к порядку.
|
||||||
|
|
||||||
|
- Почему связный список всегда медленен при поиске.
|
||||||
|
|
||||||
|
- Как удаление работает в каждой структуре.
|
||||||
|
|
||||||
|
* Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.*
|
||||||
|
|
||||||
|
## Задание: Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)
|
||||||
|
|
||||||
|
### Цель работы
|
||||||
|
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 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()` 5–10 раз, усреднить время, количество посещённых клеток, длину пути.
|
||||||
|
- Записать результаты в 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
0
SavelevMI/428.md
Normal file
0
ShulpinIN/428.md
Normal file
0
ShulpinIN/428.md
Normal file
0
SimonovaMS/428.md
Normal file
0
SimonovaMS/428.md
Normal file
0
SimonovaMS/428.txt
Normal file
0
SimonovaMS/428.txt
Normal file
0
Smirnovvs/428.txt
Normal file
0
Smirnovvs/428.txt
Normal file
0
SobolevNS/426
Normal file
0
SobolevNS/426
Normal file
0
SokolovEN/426
Normal file
0
SokolovEN/426
Normal file
0
SokolovNE/428b.md.txt
Normal file
0
SokolovNE/428b.md.txt
Normal file
0
SolovevDD/425.md
Normal file
0
SolovevDD/425.md
Normal file
0
SolovevDS/428b.md
Normal file
0
SolovevDS/428b.md
Normal file
1
SorokinAD/428.md
Normal file
1
SorokinAD/428.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
1
|
||||||
1
VaravinVV/428b
Normal file
1
VaravinVV/428b
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b
|
||||||
0
VarnakovAA/429.md
Normal file
0
VarnakovAA/429.md
Normal file
0
VildyaevAV/426
Normal file
0
VildyaevAV/426
Normal file
0
VolkovVA/428b.md
Normal file
0
VolkovVA/428b.md
Normal file
0
YanyaevAA/428b.md
Normal file
0
YanyaevAA/428b.md
Normal file
1
YaroslavtsevAS/428.md
Normal file
1
YaroslavtsevAS/428.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428
|
||||||
0
ZelentsovAV/428b.md
Normal file
0
ZelentsovAV/428b.md
Normal file
0
ZhuravlevDV/425.txt
Normal file
0
ZhuravlevDV/425.txt
Normal file
0
agafonovdm/425.txt
Normal file
0
agafonovdm/425.txt
Normal file
0
anikinvd/428.md
Normal file
0
anikinvd/428.md
Normal file
0
chizhikovaSM/428.md
Normal file
0
chizhikovaSM/428.md
Normal file
0
duznb/429.md.txt
Normal file
0
duznb/429.md.txt
Normal file
0
dyachenkoas/428
Normal file
0
dyachenkoas/428
Normal file
0
famutdinovmd/428b.md
Normal file
0
famutdinovmd/428b.md
Normal file
1
filippovavm/427
Normal file
1
filippovavm/427
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
427
|
||||||
0
fomichevks/426.md.txt
Normal file
0
fomichevks/426.md.txt
Normal file
0
groshevava/426.md.txt
Normal file
0
groshevava/426.md.txt
Normal file
1
ivanchenkoam/427.txt
Normal file
1
ivanchenkoam/427.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
856
|
||||||
0
ivantsovma/428.txt
Normal file
0
ivantsovma/428.txt
Normal file
0
kalinovskiymi/428
Normal file
0
kalinovskiymi/428
Normal file
0
kolesovve/427.md
Normal file
0
kolesovve/427.md
Normal file
BIN
komissarovgo/427.md
Normal file
BIN
komissarovgo/427.md
Normal file
Binary file not shown.
1
konnovaea/429
Normal file
1
konnovaea/429
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
429
|
||||||
0
kornevma/426.md
Normal file
0
kornevma/426.md
Normal file
0
krasnovia/429.txt
Normal file
0
krasnovia/429.txt
Normal file
1
kuznetsovTD/428b.md
Normal file
1
kuznetsovTD/428b.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b.md
|
||||||
0
lomakinae/426
Normal file
0
lomakinae/426
Normal file
0
meosyam/428.md.txt
Normal file
0
meosyam/428.md.txt
Normal file
2
morozovns/1.py
Normal file
2
morozovns/1.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
print("Zadanie adin!!11!adin!11!")
|
||||||
|
print("patch")
|
||||||
1
morozovns/429
Normal file
1
morozovns/429
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
429
|
||||||
1
nehoroshevaa/428b.md
Normal file
1
nehoroshevaa/428b.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
428b
|
||||||
0
nikitovie/425.txt
Normal file
0
nikitovie/425.txt
Normal file
BIN
nikolaevda/427.md
Normal file
BIN
nikolaevda/427.md
Normal file
Binary file not shown.
0
novikovsd/428
Normal file
0
novikovsd/428
Normal file
0
osininyai/427.md
Normal file
0
osininyai/427.md
Normal file
6
osipovamd/428.md
Normal file
6
osipovamd/428.md
Normal 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
petryaninyas/426.md
Normal file
0
petryaninyas/426.md
Normal file
0
pogodinda/427.md.txt
Normal file
0
pogodinda/427.md.txt
Normal file
1
pomelovsd/427
Normal file
1
pomelovsd/427
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
427
|
||||||
BIN
raskatovia/429.md
Normal file
BIN
raskatovia/429.md
Normal file
Binary file not shown.
0
romanovpv/427.md
Normal file
0
romanovpv/427.md
Normal file
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user