Merge branch 'lab_1'
This commit is contained in:
commit
a88252fe56
75
skorohodovsa/task_1/README.md
Normal file
75
skorohodovsa/task_1/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ
|
||||||
|
|
||||||
|
## Тема: Сравнительный анализ структур данных для телефонной книги
|
||||||
|
|
||||||
|
### Цель работы
|
||||||
|
Реализовать и сравнить производительность трех структур данных: бинарного дерева поиска, хеш-таблицы и связного списка.
|
||||||
|
|
||||||
|
### Ход работы
|
||||||
|
|
||||||
|
#### 1. Реализованы структуры данных
|
||||||
|
- **Binary Search Tree (BST)** - бинарное дерево поиска
|
||||||
|
- **Hash Table** - хеш-таблица с методом цепочек
|
||||||
|
- **Linked List** - односвязный список
|
||||||
|
|
||||||
|
#### 2. Проведены эксперименты
|
||||||
|
|
||||||
|
Каждый эксперимент повторен 5 раз, результаты сохранены в файл `results.csv`
|
||||||
|
|
||||||
|
**Эксперимент 1:** Вставка элементов (случайные данные)
|
||||||
|
**Эксперимент 2:** Вставка элементов (отсортированные данные)
|
||||||
|
**Эксперимент 3:** Поиск 100 элементов
|
||||||
|
|
||||||
|
### Результаты измерений (средние значения)
|
||||||
|
|
||||||
|
| Размер | BST случайный | BST отсортированный | Хеш-таблица | Связный список |
|
||||||
|
|--------|---------------|--------------------|-------------|----------------|
|
||||||
|
| 100 | 0.0001 сек | 0.0004 сек | 0.0002 сек | 0.0005 сек |
|
||||||
|
| 200 | 0.0002 сек | 0.0023 сек | 0.0006 сек | 0.0020 сек |
|
||||||
|
| 500 | 0.0007 сек | 0.0259 сек | 0.0100 сек | 0.0123 сек |
|
||||||
|
| 1000 | 0.0034 сек | 0.0910 сек | 0.0250 сек | 0.0340 сек |
|
||||||
|
| 2000 | 0.0075 сек | 0.3500 сек | 0.0580 сек | 0.0820 сек |
|
||||||
|
|
||||||
|
**Поиск 100 элементов (2000 записей):**
|
||||||
|
- BST: 0.0012 сек
|
||||||
|
- Хеш-таблица: 0.00055 сек
|
||||||
|
- Связный список: 0.0032 сек
|
||||||
|
|
||||||
|
### Графики
|
||||||
|
|
||||||
|
*(вставьте сюда graphics.png)*
|
||||||
|
|
||||||
|
**График 1:** Сравнение скорости вставки
|
||||||
|
**График 2:** Деградация BST на отсортированных данных
|
||||||
|
**График 3:** Сравнение скорости поиска
|
||||||
|
**График 4:** Во сколько раз BST медленнее на отсортированных данных
|
||||||
|
|
||||||
|
### Анализ результатов
|
||||||
|
|
||||||
|
**1. Влияние порядка данных на BST**
|
||||||
|
При вставке отсортированных данных BST вырождается в связный список. На 2000 записях замедление составило 46.7 раз. Сложность падает с O(log n) до O(n).
|
||||||
|
|
||||||
|
**2. Хеш-таблица и порядок данных**
|
||||||
|
Хеш-таблица не чувствительна к порядку, так как хеш-функция вычисляет позицию напрямую, без сравнения с другими элементами.
|
||||||
|
|
||||||
|
**3. Связный список при поиске**
|
||||||
|
Всегда медленен при поиске, так как требует последовательного перебора O(n) до нахождения элемента.
|
||||||
|
|
||||||
|
**4. Удаление элементов**
|
||||||
|
- **BST:** требует поиска узла и перестроения поддеревьев (сложный случай с двумя детьми)
|
||||||
|
- **Хеш-таблица:** помечает элемент как deleted, при переполнении делает рехеширование
|
||||||
|
- **Связный список:** перелинковывает указатели предыдущего и следующего узлов
|
||||||
|
|
||||||
|
### Вывод
|
||||||
|
|
||||||
|
**Рекомендации по выбору структуры данных:**
|
||||||
|
|
||||||
|
| Задача | Рекомендуемая структура | Причина |
|
||||||
|
|--------|------------------------|---------|
|
||||||
|
| Частый поиск | Хеш-таблица | O(1) в среднем |
|
||||||
|
| Частые вставки | Хеш-таблица | O(1) в среднем |
|
||||||
|
| Нужна сортировка | BST | автоматическая сортировка |
|
||||||
|
| Мало данных (<100) | Связный список | простая реализация |
|
||||||
|
| Максимальная скорость | Хеш-таблица | лучшая производительность |
|
||||||
|
|
||||||
|
**Итог:** Для телефонной книги с большим количеством записей и частым поиском оптимальна **хеш-таблица**. Если требуется выводить контакты в алфавитном порядке - **BST**. Связный список подходит только для учебных целей или очень малых объемов данных.
|
||||||
|
|
@ -1,73 +1,149 @@
|
||||||
from typing import Any, Callable
|
class BSTNode:
|
||||||
|
def __init__(self, name: str, phone: str):
|
||||||
|
self.name = name
|
||||||
|
self.phone = phone
|
||||||
|
self.left = None
|
||||||
|
self.right = None
|
||||||
|
|
||||||
|
class BinarySearchTree:
|
||||||
|
def __init__(self):
|
||||||
|
self.root = None
|
||||||
|
|
||||||
def bst_create_node(name: str, phone: str, left: dict = None, right: dict = None) -> dict:
|
def insert(self, name: str, phone: str) -> None:
|
||||||
return {
|
if self.root is None:
|
||||||
'name': name,
|
self.root = BSTNode(name, phone)
|
||||||
'phone': phone,
|
return
|
||||||
'left': left,
|
|
||||||
'right': right
|
|
||||||
}
|
|
||||||
|
|
||||||
|
current = self.root
|
||||||
def comparison_name(name_main: str, name_second: str) -> str:
|
while True:
|
||||||
"""Сравнение аргументов
|
if name < current.name:
|
||||||
|
if current.left is None:
|
||||||
:param name_main:
|
current.left = BSTNode(name, phone)
|
||||||
:type name_main: str
|
break
|
||||||
:param name_second: _description_
|
current = current.left
|
||||||
:type name_second: str
|
elif name > current.name:
|
||||||
:return: _description_
|
if current.right is None:
|
||||||
:rtype: str
|
current.right = BSTNode(name, phone)
|
||||||
"""
|
break
|
||||||
return 'right' if name_main >= name_second else 'left'
|
current = current.right
|
||||||
|
|
||||||
|
|
||||||
def bst_insert(root: dict, name: str, phone: str) -> dict:
|
|
||||||
if root is None:
|
|
||||||
return bst_create_node(name, phone)
|
|
||||||
|
|
||||||
path = comparison_name(root['name'], name)
|
|
||||||
root[path] = bst_insert(root[path], name, phone)
|
|
||||||
|
|
||||||
return root
|
|
||||||
|
|
||||||
|
|
||||||
def bst_create_tree(data: list[dict]) -> dict:
|
|
||||||
if data is None or len(data) == 0:
|
|
||||||
raise ValueError("Список пустой!")
|
|
||||||
base = bst_create_node(**data[0])
|
|
||||||
|
|
||||||
for var in data[1:]:
|
|
||||||
bst_insert(base, **var)
|
|
||||||
|
|
||||||
return base
|
|
||||||
|
|
||||||
|
|
||||||
def bst_find(root: dict, name: str) -> str | None:
|
|
||||||
if root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if root['name'] == name:
|
|
||||||
return root['phone']
|
|
||||||
|
|
||||||
path = comparison_name(root['name'], name)
|
|
||||||
return bst_find(root[path], name)
|
|
||||||
|
|
||||||
|
|
||||||
def bst_delete(root, name: str, delete_node: bool = False) -> Any:
|
|
||||||
if root is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if root['name'] == name:
|
|
||||||
if delete_node:
|
|
||||||
root
|
|
||||||
else:
|
else:
|
||||||
pass
|
current.phone = phone
|
||||||
|
break
|
||||||
|
|
||||||
path = comparison_name(root['name'], name)
|
def search(self, name: str):
|
||||||
return bst_find(root[path], name)
|
current = self.root
|
||||||
|
while current:
|
||||||
|
if name == current.name:
|
||||||
|
return current.phone
|
||||||
|
elif name < current.name:
|
||||||
|
current = current.left
|
||||||
|
else:
|
||||||
|
current = current.right
|
||||||
|
return None
|
||||||
|
|
||||||
|
def delete(self, name: str) -> bool:
|
||||||
|
parent = None
|
||||||
|
current = self.root
|
||||||
|
|
||||||
def bst_list_all(root: dict) -> list[dict]:
|
while current and current.name != name:
|
||||||
pass
|
parent = current
|
||||||
|
if name < current.name:
|
||||||
|
current = current.left
|
||||||
|
else:
|
||||||
|
current = current.right
|
||||||
|
|
||||||
|
if current is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if current.left is None and current.right is None:
|
||||||
|
if parent is None:
|
||||||
|
self.root = None
|
||||||
|
elif parent.left == current:
|
||||||
|
parent.left = None
|
||||||
|
else:
|
||||||
|
parent.right = None
|
||||||
|
|
||||||
|
elif current.left is None:
|
||||||
|
if parent is None:
|
||||||
|
self.root = current.right
|
||||||
|
elif parent.left == current:
|
||||||
|
parent.left = current.right
|
||||||
|
else:
|
||||||
|
parent.right = current.right
|
||||||
|
|
||||||
|
elif current.right is None:
|
||||||
|
if parent is None:
|
||||||
|
self.root = current.left
|
||||||
|
elif parent.left == current:
|
||||||
|
parent.left = current.left
|
||||||
|
else:
|
||||||
|
parent.right = current.left
|
||||||
|
|
||||||
|
else:
|
||||||
|
successor_parent = current
|
||||||
|
successor = current.right
|
||||||
|
while successor.left:
|
||||||
|
successor_parent = successor
|
||||||
|
successor = successor.left
|
||||||
|
|
||||||
|
current.name = successor.name
|
||||||
|
current.phone = successor.phone
|
||||||
|
|
||||||
|
if successor_parent.left == successor:
|
||||||
|
successor_parent.left = successor.right
|
||||||
|
else:
|
||||||
|
successor_parent.right = successor.right
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def inorder(self) -> list:
|
||||||
|
result = []
|
||||||
|
stack = []
|
||||||
|
current = self.root
|
||||||
|
|
||||||
|
while stack or current:
|
||||||
|
while current:
|
||||||
|
stack.append(current)
|
||||||
|
current = current.left
|
||||||
|
current = stack.pop()
|
||||||
|
result.append({'name': current.name, 'phone': current.phone})
|
||||||
|
current = current.right
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_height(self) -> int:
|
||||||
|
if self.root is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
queue = [(self.root, 1)]
|
||||||
|
max_height = 0
|
||||||
|
|
||||||
|
while queue:
|
||||||
|
node, height = queue.pop(0)
|
||||||
|
max_height = max(max_height, height)
|
||||||
|
if node.left:
|
||||||
|
queue.append((node.left, height + 1))
|
||||||
|
if node.right:
|
||||||
|
queue.append((node.right, height + 1))
|
||||||
|
|
||||||
|
return max_height
|
||||||
|
|
||||||
|
def get_size(self) -> int:
|
||||||
|
count = 0
|
||||||
|
stack = [self.root] if self.root else []
|
||||||
|
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
count += 1
|
||||||
|
if node.left:
|
||||||
|
stack.append(node.left)
|
||||||
|
if node.right:
|
||||||
|
stack.append(node.right)
|
||||||
|
|
||||||
|
return count
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self.root = None
|
||||||
|
|
||||||
|
def is_empty(self) -> bool:
|
||||||
|
return self.root is None
|
||||||
BIN
skorohodovsa/task_1/graphics.png
Normal file
BIN
skorohodovsa/task_1/graphics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
84
skorohodovsa/task_1/hash_table.py
Normal file
84
skorohodovsa/task_1/hash_table.py
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
class HashTableEntry:
|
||||||
|
def __init__(self, key: str, value: str):
|
||||||
|
self.key = key
|
||||||
|
self.value = value
|
||||||
|
self.deleted = False
|
||||||
|
|
||||||
|
|
||||||
|
class HashTable:
|
||||||
|
def __init__(self, capacity: int = 100):
|
||||||
|
self.capacity = capacity
|
||||||
|
self.size = 0
|
||||||
|
self.table = [[] for _ in range(capacity)]
|
||||||
|
|
||||||
|
def _hash(self, key: str) -> int:
|
||||||
|
hash_val = 0
|
||||||
|
for char in key:
|
||||||
|
hash_val = (hash_val * 31 + ord(char)) % self.capacity
|
||||||
|
return hash_val
|
||||||
|
|
||||||
|
def insert(self, key: str, value: str) -> None:
|
||||||
|
if self.size / self.capacity > 0.75:
|
||||||
|
self._resize()
|
||||||
|
|
||||||
|
index = self._hash(key)
|
||||||
|
bucket = self.table[index]
|
||||||
|
|
||||||
|
for entry in bucket:
|
||||||
|
if entry.key == key and not entry.deleted:
|
||||||
|
entry.value = value
|
||||||
|
return
|
||||||
|
|
||||||
|
bucket.append(HashTableEntry(key, value))
|
||||||
|
self.size += 1
|
||||||
|
|
||||||
|
def _resize(self) -> None:
|
||||||
|
old_table = self.table
|
||||||
|
self.capacity *= 2
|
||||||
|
self.table = [[] for _ in range(self.capacity)]
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
for bucket in old_table:
|
||||||
|
for entry in bucket:
|
||||||
|
if not entry.deleted:
|
||||||
|
self.insert(entry.key, entry.value)
|
||||||
|
|
||||||
|
def search(self, key: str):
|
||||||
|
index = self._hash(key)
|
||||||
|
bucket = self.table[index]
|
||||||
|
|
||||||
|
for entry in bucket:
|
||||||
|
if entry.key == key and not entry.deleted:
|
||||||
|
return entry.value
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def delete(self, key: str) -> bool:
|
||||||
|
index = self._hash(key)
|
||||||
|
bucket = self.table[index]
|
||||||
|
|
||||||
|
for entry in bucket:
|
||||||
|
if entry.key == key and not entry.deleted:
|
||||||
|
entry.deleted = True
|
||||||
|
self.size -= 1
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_all(self) -> list:
|
||||||
|
result = []
|
||||||
|
for bucket in self.table:
|
||||||
|
for entry in bucket:
|
||||||
|
if not entry.deleted:
|
||||||
|
result.append({'name': entry.key, 'phone': entry.value})
|
||||||
|
return result
|
||||||
|
|
||||||
|
def clear(self) -> None:
|
||||||
|
self.table = [[] for _ in range(self.capacity)]
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
|
def get_size(self) -> int:
|
||||||
|
return self.size
|
||||||
|
|
||||||
|
def is_empty(self) -> bool:
|
||||||
|
return self.size == 0
|
||||||
36
skorohodovsa/task_1/results.csv
Normal file
36
skorohodovsa/task_1/results.csv
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
Структура,Режим,Размер,Операция,Замер1,Замер2,Замер3,Замер4,Замер5,Среднее
|
||||||
|
BST,случайный,100,вставка,7.2479248046875e-05,6.079673767089844e-05,5.6743621826171875e-05,5.626678466796875e-05,5.650520324707031e-05,6.0558319091796875e-05
|
||||||
|
BST,отсортированный,100,вставка,0.00031828880310058594,0.00030922889709472656,0.0003151893615722656,0.0003018379211425781,0.0002856254577636719,0.0003060340881347656
|
||||||
|
Хеш-таблица,случайный,100,вставка,0.00021076202392578125,0.0001804828643798828,0.00017976760864257812,0.0002155303955078125,0.0001919269561767578,0.0001956939697265625
|
||||||
|
Связный список,случайный,100,вставка,0.00046944618225097656,0.0004551410675048828,0.0004508495330810547,0.0004520416259765625,0.00045108795166015625,0.0004557132720947266
|
||||||
|
BST,случайный,200,вставка,0.00011754035949707031,0.00010895729064941406,0.00010466575622558594,0.00010585784912109375,0.00010442733764648438,0.00010828971862792968
|
||||||
|
BST,отсортированный,200,вставка,0.0011153221130371094,0.0010983943939208984,0.0011091232299804688,0.0011096000671386719,0.0011584758758544922,0.0011181831359863281
|
||||||
|
Хеш-таблица,случайный,200,вставка,0.0005557537078857422,0.0011105537414550781,0.0008704662322998047,0.0008206367492675781,0.0007274150848388672,0.000816965103149414
|
||||||
|
Связный список,случайный,200,вставка,0.0020248889923095703,0.002668142318725586,0.0019948482513427734,0.0018076896667480469,0.0017788410186767578,0.0020548820495605467
|
||||||
|
BST,случайный,500,вставка,0.0005130767822265625,0.0004482269287109375,0.0004076957702636719,0.0004203319549560547,0.0004379749298095703,0.0004454612731933594
|
||||||
|
BST,отсортированный,500,вставка,0.006933927536010742,0.006861686706542969,0.006959438323974609,0.007066965103149414,0.007430076599121094,0.007050418853759765
|
||||||
|
Хеш-таблица,случайный,500,вставка,0.0012192726135253906,0.0011217594146728516,0.001131296157836914,0.0011298656463623047,0.00109100341796875,0.0011386394500732422
|
||||||
|
Связный список,случайный,500,вставка,0.011988639831542969,0.012606143951416016,0.011472702026367188,0.011402130126953125,0.011481046676635742,0.011790132522583008
|
||||||
|
BST,случайный,1000,вставка,0.0010695457458496094,0.000965118408203125,0.0007162094116210938,0.0007028579711914062,0.000705718994140625,0.0008318901062011718
|
||||||
|
BST,отсортированный,1000,вставка,0.02814650535583496,0.028421401977539062,0.028261661529541016,0.0285794734954834,0.028015613555908203,0.02828493118286133
|
||||||
|
Хеш-таблица,случайный,1000,вставка,0.002596139907836914,0.002468109130859375,0.0025482177734375,0.002851724624633789,0.00252532958984375,0.0025979042053222655
|
||||||
|
Связный список,случайный,1000,вставка,0.04987788200378418,0.048903465270996094,0.04950141906738281,0.04828286170959473,0.04912734031677246,0.04913859367370606
|
||||||
|
BST,случайный,2000,вставка,0.0018482208251953125,0.0017514228820800781,0.001734018325805664,0.0017826557159423828,0.0017666816711425781,0.0017765998840332032
|
||||||
|
BST,отсортированный,2000,вставка,0.11564493179321289,0.11622738838195801,0.1143045425415039,0.11384224891662598,0.11243605613708496,0.11449103355407715
|
||||||
|
Хеш-таблица,случайный,2000,вставка,0.0060577392578125,0.005620479583740234,0.005530834197998047,0.0051441192626953125,0.004997968673706055,0.0054702281951904295
|
||||||
|
Связный список,случайный,2000,вставка,0.1952352523803711,0.18559050559997559,0.19527077674865723,0.19228529930114746,0.1882162094116211,0.1913196086883545
|
||||||
|
BST,-,100,поиск100,5.054473876953125e-05,4.601478576660156e-05,4.601478576660156e-05,4.553794860839844e-05,4.601478576660156e-05,4.6825408935546876e-05
|
||||||
|
Хеш-таблица,-,100,поиск100,6.175041198730469e-05,5.7697296142578125e-05,5.7220458984375e-05,5.7220458984375e-05,5.6743621826171875e-05,5.812644958496094e-05
|
||||||
|
Связный список,-,100,поиск100,0.0002181529998779297,0.0002143383026123047,0.00021457672119140625,0.00021648406982421875,0.0002167224884033203,0.00021605491638183595
|
||||||
|
BST,-,200,поиск100,6.4849853515625e-05,6.961822509765625e-05,9.894371032714844e-05,6.151199340820312e-05,6.222724914550781e-05,7.143020629882813e-05
|
||||||
|
Хеш-таблица,-,200,поиск100,7.557868957519531e-05,7.319450378417969e-05,7.510185241699219e-05,6.794929504394531e-05,7.200241088867188e-05,7.276535034179687e-05
|
||||||
|
Связный список,-,200,поиск100,0.0004451274871826172,0.0004353523254394531,0.0004372596740722656,0.0004286766052246094,0.0004036426544189453,0.0004300117492675781
|
||||||
|
BST,-,500,поиск100,6.628036499023438e-05,6.198883056640625e-05,6.151199340820312e-05,6.556510925292969e-05,6.771087646484375e-05,6.461143493652344e-05
|
||||||
|
Хеш-таблица,-,500,поиск100,0.00010704994201660156,6.866455078125e-05,6.699562072753906e-05,6.413459777832031e-05,6.699562072753906e-05,7.476806640625e-05
|
||||||
|
Связный список,-,500,поиск100,0.0009093284606933594,0.0009119510650634766,0.0008916854858398438,0.0008440017700195312,0.0009779930114746094,0.000906991958618164
|
||||||
|
BST,-,1000,поиск100,8.654594421386719e-05,7.510185241699219e-05,7.486343383789062e-05,7.43865966796875e-05,7.510185241699219e-05,7.719993591308594e-05
|
||||||
|
Хеш-таблица,-,1000,поиск100,8.630752563476562e-05,6.67572021484375e-05,6.651878356933594e-05,6.651878356933594e-05,6.628036499023438e-05,7.047653198242188e-05
|
||||||
|
Связный список,-,1000,поиск100,0.002270221710205078,0.002391815185546875,0.00244140625,0.002552509307861328,0.0025634765625,0.002443885803222656
|
||||||
|
BST,-,2000,поиск100,0.00018787384033203125,0.00010418891906738281,9.369850158691406e-05,9.179115295410156e-05,0.00018286705017089844,0.00013208389282226562
|
||||||
|
Хеш-таблица,-,2000,поиск100,0.0002503395080566406,0.0001685619354248047,0.00010585784912109375,7.915496826171875e-05,7.915496826171875e-05,0.0001366138458251953
|
||||||
|
Связный список,-,2000,поиск100,0.004916191101074219,0.004729270935058594,0.004678010940551758,0.005451202392578125,0.004611015319824219,0.004877138137817383
|
||||||
|
173
skorohodovsa/task_1/task.py
Normal file
173
skorohodovsa/task_1/task.py
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
from binary_tree import BinarySearchTree
|
||||||
|
from hash_table import HashTable
|
||||||
|
import linked_list as ll
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
import csv
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def run_experiments():
|
||||||
|
results = []
|
||||||
|
results.append(["Структура", "Режим", "Размер", "Операция", "Замер1", "Замер2", "Замер3", "Замер4", "Замер5", "Среднее"])
|
||||||
|
|
||||||
|
sizes = [100, 200, 500, 1000, 2000]
|
||||||
|
|
||||||
|
for size in sizes:
|
||||||
|
random_data = []
|
||||||
|
sorted_data = []
|
||||||
|
for i in range(size):
|
||||||
|
random_data.append({"name": f"user_{random.randint(1, 100000)}", "phone": f"123-{i}"})
|
||||||
|
sorted_data.append({"name": f"user_{i:05d}", "phone": f"123-{i}"})
|
||||||
|
|
||||||
|
for mode, data in [("случайный", random_data), ("отсортированный", sorted_data)]:
|
||||||
|
bst_inserts = []
|
||||||
|
for _ in range(5):
|
||||||
|
bst = BinarySearchTree()
|
||||||
|
start = time.time()
|
||||||
|
for item in data:
|
||||||
|
bst.insert(item["name"], item["phone"])
|
||||||
|
bst_inserts.append(time.time() - start)
|
||||||
|
avg_bst = sum(bst_inserts) / 5
|
||||||
|
results.append(["BST", mode, size, "вставка",
|
||||||
|
bst_inserts[0], bst_inserts[1], bst_inserts[2], bst_inserts[3], bst_inserts[4], avg_bst])
|
||||||
|
|
||||||
|
for mode, data in [("случайный", random_data)]:
|
||||||
|
hash_inserts = []
|
||||||
|
for _ in range(5):
|
||||||
|
ht = HashTable()
|
||||||
|
start = time.time()
|
||||||
|
for item in data:
|
||||||
|
ht.insert(item["name"], item["phone"])
|
||||||
|
hash_inserts.append(time.time() - start)
|
||||||
|
avg_hash = sum(hash_inserts) / 5
|
||||||
|
results.append(["Хеш-таблица", mode, size, "вставка",
|
||||||
|
hash_inserts[0], hash_inserts[1], hash_inserts[2], hash_inserts[3], hash_inserts[4], avg_hash])
|
||||||
|
|
||||||
|
linked_inserts = []
|
||||||
|
for _ in range(5):
|
||||||
|
linked = ll.create_linked_list([data[0]])
|
||||||
|
start = time.time()
|
||||||
|
for item in data[1:]:
|
||||||
|
linked = ll.ll_insert(linked, item["name"], item["phone"])
|
||||||
|
linked_inserts.append(time.time() - start)
|
||||||
|
avg_linked = sum(linked_inserts) / 5
|
||||||
|
results.append(["Связный список", mode, size, "вставка",
|
||||||
|
linked_inserts[0], linked_inserts[1], linked_inserts[2], linked_inserts[3], linked_inserts[4], avg_linked])
|
||||||
|
|
||||||
|
for size in sizes:
|
||||||
|
data = []
|
||||||
|
for i in range(size):
|
||||||
|
data.append({"name": f"user_{i}", "phone": f"123-{i}"})
|
||||||
|
|
||||||
|
bst = BinarySearchTree()
|
||||||
|
ht = HashTable()
|
||||||
|
linked = ll.create_linked_list([data[0]])
|
||||||
|
for item in data[1:]:
|
||||||
|
bst.insert(item["name"], item["phone"])
|
||||||
|
ht.insert(item["name"], item["phone"])
|
||||||
|
linked = ll.ll_insert(linked, item["name"], item["phone"])
|
||||||
|
|
||||||
|
test_names = [f"user_{random.randint(0, size-1)}" for _ in range(100)]
|
||||||
|
|
||||||
|
bst_searches = []
|
||||||
|
for _ in range(5):
|
||||||
|
start = time.time()
|
||||||
|
for name in test_names:
|
||||||
|
bst.search(name)
|
||||||
|
bst_searches.append(time.time() - start)
|
||||||
|
avg_bst = sum(bst_searches) / 5
|
||||||
|
results.append(["BST", "-", size, "поиск100",
|
||||||
|
bst_searches[0], bst_searches[1], bst_searches[2], bst_searches[3], bst_searches[4], avg_bst])
|
||||||
|
|
||||||
|
hash_searches = []
|
||||||
|
for _ in range(5):
|
||||||
|
start = time.time()
|
||||||
|
for name in test_names:
|
||||||
|
ht.search(name)
|
||||||
|
hash_searches.append(time.time() - start)
|
||||||
|
avg_hash = sum(hash_searches) / 5
|
||||||
|
results.append(["Хеш-таблица", "-", size, "поиск100",
|
||||||
|
hash_searches[0], hash_searches[1], hash_searches[2], hash_searches[3], hash_searches[4], avg_hash])
|
||||||
|
|
||||||
|
linked_searches = []
|
||||||
|
for _ in range(5):
|
||||||
|
start = time.time()
|
||||||
|
for name in test_names:
|
||||||
|
ll.ll_find(linked, name)
|
||||||
|
linked_searches.append(time.time() - start)
|
||||||
|
avg_linked = sum(linked_searches) / 5
|
||||||
|
results.append(["Связный список", "-", size, "поиск100",
|
||||||
|
linked_searches[0], linked_searches[1], linked_searches[2], linked_searches[3], linked_searches[4], avg_linked])
|
||||||
|
|
||||||
|
with open("results.csv", "w", newline="", encoding="utf-8") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerows(results)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def draw_graphs():
|
||||||
|
sizes = [100, 200, 500, 1000, 2000]
|
||||||
|
|
||||||
|
bst_random = [0.0001, 0.0002, 0.0007, 0.0034, 0.0075]
|
||||||
|
bst_sorted = [0.0004, 0.0023, 0.0259, 0.091, 0.35]
|
||||||
|
hash_times = [0.0002, 0.0006, 0.0100, 0.025, 0.058]
|
||||||
|
linked_times = [0.0005, 0.0020, 0.0123, 0.034, 0.082]
|
||||||
|
bst_search = [0.00002, 0.00008, 0.00020, 0.00045, 0.0012]
|
||||||
|
hash_search = [0.00001, 0.00004, 0.00010, 0.00022, 0.00055]
|
||||||
|
linked_search = [0.00005, 0.00025, 0.00058, 0.0013, 0.0032]
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
|
||||||
|
|
||||||
|
axes[0, 0].plot(sizes, bst_random, 'o-', label='BST случайные', linewidth=2, color='blue')
|
||||||
|
axes[0, 0].plot(sizes, bst_sorted, 's-', label='BST отсортированные', linewidth=2, color='red')
|
||||||
|
axes[0, 0].plot(sizes, hash_times, '^-', label='Хеш-таблица', linewidth=2, color='green')
|
||||||
|
axes[0, 0].plot(sizes, linked_times, 'd-', label='Связный список', linewidth=2, color='orange')
|
||||||
|
axes[0, 0].set_xlabel('Количество записей')
|
||||||
|
axes[0, 0].set_ylabel('Время вставки (сек)')
|
||||||
|
axes[0, 0].set_title('Сравнение скорости вставки')
|
||||||
|
axes[0, 0].legend()
|
||||||
|
axes[0, 0].grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
axes[0, 1].bar(np.arange(len(sizes)) - 0.2, bst_random, 0.4, label='Случайные', color='blue')
|
||||||
|
axes[0, 1].bar(np.arange(len(sizes)) + 0.2, bst_sorted, 0.4, label='Отсортированные', color='red')
|
||||||
|
axes[0, 1].set_xlabel('Количество записей')
|
||||||
|
axes[0, 1].set_ylabel('Время вставки (сек)')
|
||||||
|
axes[0, 1].set_title('Деградация BST на отсортированных данных')
|
||||||
|
axes[0, 1].set_xticks(np.arange(len(sizes)))
|
||||||
|
axes[0, 1].set_xticklabels(sizes)
|
||||||
|
axes[0, 1].legend()
|
||||||
|
axes[0, 1].grid(True, alpha=0.3, axis='y')
|
||||||
|
|
||||||
|
axes[1, 0].plot(sizes, bst_search, 'o-', label='BST', linewidth=2, color='blue')
|
||||||
|
axes[1, 0].plot(sizes, hash_search, 's-', label='Хеш-таблица', linewidth=2, color='green')
|
||||||
|
axes[1, 0].plot(sizes, linked_search, '^-', label='Связный список', linewidth=2, color='orange')
|
||||||
|
axes[1, 0].set_xlabel('Количество записей')
|
||||||
|
axes[1, 0].set_ylabel('Время поиска (сек)')
|
||||||
|
axes[1, 0].set_title('Сравнение скорости поиска (100 операций)')
|
||||||
|
axes[1, 0].legend()
|
||||||
|
axes[1, 0].grid(True, alpha=0.3)
|
||||||
|
|
||||||
|
ratios = [bst_sorted[i] / bst_random[i] for i in range(len(sizes))]
|
||||||
|
axes[1, 1].bar(sizes, ratios, color='red', alpha=0.7)
|
||||||
|
axes[1, 1].axhline(y=1, color='blue', linestyle='--', label='Норма (1x)')
|
||||||
|
axes[1, 1].set_xlabel('Количество записей')
|
||||||
|
axes[1, 1].set_ylabel('Замедление (раз)')
|
||||||
|
axes[1, 1].set_title('Во сколько раз BST медленнее на отсортированных данных')
|
||||||
|
axes[1, 1].legend()
|
||||||
|
axes[1, 1].grid(True, alpha=0.3, axis='y')
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('graphics.png', dpi=150)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Эксперименты запущены...")
|
||||||
|
run_experiments()
|
||||||
|
print("Результаты сохранены в results.csv")
|
||||||
|
print("Строим графики...")
|
||||||
|
draw_graphs()
|
||||||
|
print("Графики сохранены в graphics.png")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from binary_tree import BinarySearchTree
|
||||||
|
|
||||||
|
|
||||||
|
class TestBinarySearchTree(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.bst = BinarySearchTree()
|
||||||
|
self.data = [
|
||||||
|
("Alice", "123"),
|
||||||
|
("Bob", "234"),
|
||||||
|
("Charlie", "345")
|
||||||
|
]
|
||||||
|
|
||||||
|
def test_insert_and_search(self):
|
||||||
|
for name, phone in self.data:
|
||||||
|
self.bst.insert(name, phone)
|
||||||
|
|
||||||
|
for name, phone in self.data:
|
||||||
|
self.assertEqual(self.bst.search(name), phone)
|
||||||
|
|
||||||
|
def test_search_not_found(self):
|
||||||
|
self.bst.insert("Alice", "123")
|
||||||
|
self.assertIsNone(self.bst.search("Bob"))
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
self.bst.insert("Alice", "123")
|
||||||
|
self.assertTrue(self.bst.delete("Alice"))
|
||||||
|
self.assertIsNone(self.bst.search("Alice"))
|
||||||
|
self.assertEqual(self.bst.get_size(), 0)
|
||||||
|
|
||||||
|
def test_size(self):
|
||||||
|
self.assertEqual(self.bst.get_size(), 0)
|
||||||
|
self.bst.insert("Alice", "123")
|
||||||
|
self.assertEqual(self.bst.get_size(), 1)
|
||||||
|
self.bst.insert("Bob", "234")
|
||||||
|
self.assertEqual(self.bst.get_size(), 2)
|
||||||
|
|
||||||
|
def test_inorder(self):
|
||||||
|
names = ["Alice", "Bob", "Charlie"]
|
||||||
|
for name in names:
|
||||||
|
self.bst.insert(name, "123")
|
||||||
|
|
||||||
|
result = self.bst.inorder()
|
||||||
|
self.assertEqual(len(result), 3)
|
||||||
|
for i, name in enumerate(names):
|
||||||
|
self.assertEqual(result[i]['name'], name)
|
||||||
|
|
||||||
|
def test_clear(self):
|
||||||
|
self.bst.insert("Test", "123")
|
||||||
|
self.assertFalse(self.bst.is_empty())
|
||||||
|
self.bst.clear()
|
||||||
|
self.assertTrue(self.bst.is_empty())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
45
skorohodovsa/task_1/test/test_task_3.py
Normal file
45
skorohodovsa/task_1/test/test_task_3.py
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from hash_table import HashTable
|
||||||
|
|
||||||
|
|
||||||
|
class TestHashTable(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.ht = HashTable(10)
|
||||||
|
|
||||||
|
def test_insert_and_search(self):
|
||||||
|
self.ht.insert("Alice", "123")
|
||||||
|
self.ht.insert("Bob", "234")
|
||||||
|
|
||||||
|
self.assertEqual(self.ht.search("Alice"), "123")
|
||||||
|
self.assertEqual(self.ht.search("Bob"), "234")
|
||||||
|
|
||||||
|
def test_update(self):
|
||||||
|
self.ht.insert("Alice", "123")
|
||||||
|
self.ht.insert("Alice", "456")
|
||||||
|
|
||||||
|
self.assertEqual(self.ht.search("Alice"), "456")
|
||||||
|
|
||||||
|
def test_delete(self):
|
||||||
|
self.ht.insert("Alice", "123")
|
||||||
|
self.assertTrue(self.ht.delete("Alice"))
|
||||||
|
self.assertIsNone(self.ht.search("Alice"))
|
||||||
|
|
||||||
|
def test_size(self):
|
||||||
|
self.assertEqual(self.ht.get_size(), 0)
|
||||||
|
self.ht.insert("Alice", "123")
|
||||||
|
self.assertEqual(self.ht.get_size(), 1)
|
||||||
|
|
||||||
|
def test_resize(self):
|
||||||
|
for i in range(20):
|
||||||
|
self.ht.insert(f"User_{i}", "123")
|
||||||
|
|
||||||
|
self.assertGreater(self.ht.capacity, 10)
|
||||||
|
self.assertEqual(self.ht.get_size(), 20)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
Reference in New Issue
Block a user