[1] 1 лаба #173

Open
musinaa wants to merge 12 commits from musinaa/2026-rff_mp:task-1 into develop
12 changed files with 684 additions and 0 deletions

2
MusinAA/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.vscode/
*/tests/

349
MusinAA/docs/Report 1.ipynb Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

View File

@ -0,0 +1,31 @@
Структура,Режим,Вставка,Поиск,Удаление
Связанный список,Cлучайный,0.09829891600020346,0.0007068659997457871,0.0005386180000641616
Хэш-таблица,Cлучайный,0.04794800999934523,0.00069890399936412,0.00033887100016727345
Бинарное дерево,Cлучайный,0.014146466999591212,0.00019723300010809908,0.00022258500030147843
Связанный список,Отсортированный,0.16592630900049699,0.0017924130006576888,0.001010537000183831
Хэш-таблица,Отсортированный,0.04675658399992244,0.000497691000418854,0.00027706199944077525
Бинарное дерево,Отсортированный,0.098506346999784,0.001621370999600913,0.0008596789994044229
Связанный список,Cлучайный,0.07528530299987324,0.0006713170005241409,0.0004351130000941339
Хэш-таблица,Cлучайный,0.04169118899972091,0.0004370679998828564,0.0002442360000713961
Бинарное дерево,Cлучайный,0.009762656000020797,0.0001406600003974745,8.869900011632126e-05
Связанный список,Отсортированный,0.15083865700034949,0.001965620000191848,0.0009268670000892598
Хэш-таблица,Отсортированный,0.04658651899990218,0.0004731760000140639,0.00026295399948139675
Бинарное дерево,Отсортированный,0.10888835700006894,0.0032681640004739165,0.0010110960001838976
Связанный список,Cлучайный,0.09252672599996004,0.0014638780003224383,0.0009516599993730779
Хэш-таблица,Cлучайный,0.04701576600018598,0.0004413979995661066,0.00024472499990224605
Бинарное дерево,Cлучайный,0.010519597999518737,0.00015120700027182465,0.00012815900026907912
Связанный список,Отсортированный,0.15883956299967394,0.001480011000239756,0.0007378059999609832
Хэш-таблица,Отсортированный,0.043343710000044666,0.0005192710004848777,0.0002623249993121135
Бинарное дерево,Отсортированный,0.19170180800028902,0.0011184409995621536,0.0008248280000771047
Связанный список,Cлучайный,0.09595573600017815,0.0009538959993733442,0.0004928719999952591
Хэш-таблица,Cлучайный,0.04453241200008051,0.000944256999900972,0.0005029280000599101
Бинарное дерево,Cлучайный,0.011908257000868616,0.0001221530001203064,0.00011502899997140048
Связанный список,Отсортированный,0.16769071699945926,0.0015361639998445753,0.0011414199998398544
Хэш-таблица,Отсортированный,0.05018426599963277,0.0006002179998176871,0.000283696000224154
Бинарное дерево,Отсортированный,0.09999411199987662,0.0010742320000645122,0.0009550129998388002
Связанный список,Cлучайный,0.08812657299949933,0.0006700599997202517,0.0006053869992683758
Хэш-таблица,Cлучайный,0.042967892999513424,0.0005705349994968856,0.0002917279998655431
Бинарное дерево,Cлучайный,0.01326883900037501,0.00013954399946669582,0.00013297800069267396
Связанный список,Отсортированный,0.16893773900028464,0.0017602859998078202,0.0007569420004074345
Хэш-таблица,Отсортированный,0.05997269399995275,0.000543855999239895,0.0002741980006248923
Бинарное дерево,Отсортированный,0.11176624800009449,0.0010512540002309834,0.0007160159993873094
1 Структура Режим Вставка Поиск Удаление
2 Связанный список Cлучайный 0.09829891600020346 0.0007068659997457871 0.0005386180000641616
3 Хэш-таблица Cлучайный 0.04794800999934523 0.00069890399936412 0.00033887100016727345
4 Бинарное дерево Cлучайный 0.014146466999591212 0.00019723300010809908 0.00022258500030147843
5 Связанный список Отсортированный 0.16592630900049699 0.0017924130006576888 0.001010537000183831
6 Хэш-таблица Отсортированный 0.04675658399992244 0.000497691000418854 0.00027706199944077525
7 Бинарное дерево Отсортированный 0.098506346999784 0.001621370999600913 0.0008596789994044229
8 Связанный список Cлучайный 0.07528530299987324 0.0006713170005241409 0.0004351130000941339
9 Хэш-таблица Cлучайный 0.04169118899972091 0.0004370679998828564 0.0002442360000713961
10 Бинарное дерево Cлучайный 0.009762656000020797 0.0001406600003974745 8.869900011632126e-05
11 Связанный список Отсортированный 0.15083865700034949 0.001965620000191848 0.0009268670000892598
12 Хэш-таблица Отсортированный 0.04658651899990218 0.0004731760000140639 0.00026295399948139675
13 Бинарное дерево Отсортированный 0.10888835700006894 0.0032681640004739165 0.0010110960001838976
14 Связанный список Cлучайный 0.09252672599996004 0.0014638780003224383 0.0009516599993730779
15 Хэш-таблица Cлучайный 0.04701576600018598 0.0004413979995661066 0.00024472499990224605
16 Бинарное дерево Cлучайный 0.010519597999518737 0.00015120700027182465 0.00012815900026907912
17 Связанный список Отсортированный 0.15883956299967394 0.001480011000239756 0.0007378059999609832
18 Хэш-таблица Отсортированный 0.043343710000044666 0.0005192710004848777 0.0002623249993121135
19 Бинарное дерево Отсортированный 0.19170180800028902 0.0011184409995621536 0.0008248280000771047
20 Связанный список Cлучайный 0.09595573600017815 0.0009538959993733442 0.0004928719999952591
21 Хэш-таблица Cлучайный 0.04453241200008051 0.000944256999900972 0.0005029280000599101
22 Бинарное дерево Cлучайный 0.011908257000868616 0.0001221530001203064 0.00011502899997140048
23 Связанный список Отсортированный 0.16769071699945926 0.0015361639998445753 0.0011414199998398544
24 Хэш-таблица Отсортированный 0.05018426599963277 0.0006002179998176871 0.000283696000224154
25 Бинарное дерево Отсортированный 0.09999411199987662 0.0010742320000645122 0.0009550129998388002
26 Связанный список Cлучайный 0.08812657299949933 0.0006700599997202517 0.0006053869992683758
27 Хэш-таблица Cлучайный 0.042967892999513424 0.0005705349994968856 0.0002917279998655431
28 Бинарное дерево Cлучайный 0.01326883900037501 0.00013954399946669582 0.00013297800069267396
29 Связанный список Отсортированный 0.16893773900028464 0.0017602859998078202 0.0007569420004074345
30 Хэш-таблица Отсортированный 0.05997269399995275 0.000543855999239895 0.0002741980006248923
31 Бинарное дерево Отсортированный 0.11176624800009449 0.0010512540002309834 0.0007160159993873094

View File

View File

@ -0,0 +1,88 @@
"""
Двоичное дерево поиска
Узел словарь:
{'name': 'Имя', 'phone': '123', 'left': None, 'right': None}.
"""
def bst_insert(root: dict|None, name: str, phone: str) -> dict:
"""Итеративно вставляет, возвращает новый корень (если корень меняется)."""
if root == None:
return {'name': name, 'phone': phone, 'left': None, 'right': None}
# '674' < '722' == True, lol
current = root
while True:
if current['name'] == name:
current['phone'] = phone
return root
elif name < current['name']:
if current['left'] == None:
current['left'] = bst_insert(None, name, phone)
return root
else:
current = current['left']
else:
if current['right'] == None:
current['right'] = bst_insert(None, name, phone)
return root
else:
current = current['right']
# Увы, это самый лаконичный вариант, который я придумал.
def bst_find(root: dict|None, name: str) -> str|None:
"""Поиск в ширину."""
node = find_node_to_delete(root, name)
if node != None:
return node['phone']
def find_node_to_delete(root: dict|None, name: str) -> dict|None:
"""Поиск в ширину."""
while root != None:
if root['name'] == name:
return root
elif name < root['name']:
root = root['left']
else:
root = root['right']
return None
def find_minimal_child(root: dict) -> dict|None:
while root['left']:
root = root['left']
return root
def bst_delete(root: dict, name: str) -> None:
"""Удаляет узел и возвращает новый корень."""
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:
# Случай 1: нет детей или один ребенок
if root['left'] is None:
return root['right']
elif root['right'] is None:
return root['left']
# Случай 2: два ребенка
min_node = find_minimal_child(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: dict) -> list:
"""Центрированный обход.
Рекурсивно собирает записи в отсортированном порядке."""
if root is None:
return []
node_values = {"name": root['name'], "phone": root['phone']}
return bst_list_all(root['left']) + [node_values] + bst_list_all(root['right'])

View File

@ -0,0 +1,59 @@
"""
Хеш-таблица
Хранится как список buckets фиксированной длины,
каждый элемент голова связного списка (или None).
"""
from task1.structures.LinkedList import *
def hash_fun(name: str, size: int) -> int:
"""Принимает имя и возвращает индекс бакета для него."""
if size <= 0:
raise ValueError("size должен быть больше 0")
hashSum = 0
n = size+1
base = 1103 # ord('я')
for letter in name:
hashSum += ord(letter) * pow(base, n)
n -= 1
return int(hashSum) % size
def ht_insert(buckets: list|None, name: str, phone: str, blen:int = 50) -> list:
"""Возвращает новый массив бакетов
Вычисляет индекс, вызывает ll_insert для соответствующего бакета.
Функция не меняет размер массива бакетов автоматически!"""
if buckets == [] or buckets == None:
buckets = [None] * blen
# raise ValueError("Длинна buckets должна быть больше 0")
size = len(buckets)
index = hash_fun(name, size)
buckets[index] = ll_insert(buckets[index], name, phone)
return buckets
def ht_delete(buckets: list, name: str) -> list:
"""Возвращает новый массив бакетов без элемента с именем name"""
if buckets == []:
raise ValueError("Длинна buckets должна быть больше 0")
size = len(buckets)
index = hash_fun(name, size)
buckets[index] = ll_delete(buckets[index], name)
return buckets
def ht_find(buckets: list|None, name: str) -> str|None:
if buckets == [] or buckets == None:
raise ValueError("Длинна buckets должна быть больше 0")
size = len(buckets)
index = hash_fun(name, size)
return ll_find(buckets[index], name)
def ht_list_all(buckets):
"""Собирает все записи из всех бакетов и сортирует"""
allRecords = []
for bucket in buckets:
allRecords.extend(ll_list_all(bucket))
return sorted(allRecords, key=lambda x: x[0])

View File

@ -0,0 +1,63 @@
"""
Связный список (LinkedListPhoneBook)
Узел представляется словарём:
{'name': 'Имя', 'phone': '123', 'next': None}.
"""
def ll_insert(head : dict|None, name: str, phone: str) -> dict:
"""
Проходит до конца (или сразу добавляет в конец) и возвращает новую
голову (если вставка в начало) или изменяет список по ссылке.
Удобнее возвращать новую голову, если вставка может быть в начало.
"""
newNode = {'name': name, 'phone': phone, 'next': None}
if head == None:
return newNode
currentNode = head
while currentNode['next'] != None:
if currentNode['name'] == name:
currentNode['phone'] = phone
return head
currentNode = currentNode['next']
currentNode['next'] = newNode
return head
def ll_find(head : dict|None, name: str) -> str|None:
"""Ищет узел, возвращает телефон или None."""
currentNode = head
while currentNode != None:
if currentNode['name'] == name:
return currentNode['phone']
currentNode = currentNode['next']
return None
def ll_delete(head : dict|None, name: str) -> dict|None:
"""Удаляет узел, возвращает новую голову."""
if head == None:
return None
if head['name'] == name:
return head['next']
currentNode = head
while currentNode['next'] != None:
if currentNode['next']['name'] == name:
currentNode['next'] = currentNode['next']['next']
return head
currentNode = currentNode['next']
return head
def ll_list_all(head: dict|None) -> list:
"""Cобирает все записи в список и сортирует.
сортировка вынесена отдельно)."""
records = []
currentNode = head
while currentNode != None:
records.append((currentNode['name'], currentNode['phone']))
currentNode = currentNode['next']
records.sort(key=lambda item: item[0])
return records

View File

View File

View File

@ -0,0 +1,55 @@
import random
names_pool = (
"Иван", "Мария", "Петр", "Анна", "Сергей", "Елена", "Алексей", "Ольга",
"Дмитрий", "Татьяна", "Михаил", "Наталья", "Андрей", "Ирина", "Николай",
"Светлана", "Владимир", "Екатерина", "Александр", "Юлия", "Павел", "Ксения",
"Виктор", "Анастасия", "Артем", "Виктория", "Максим", "Полина", "Даниил",
"София", "Евгений", "Алиса", "Станислав", "Дарья", "Георгий", "Вероника",
"Кирилл", "Маргарита", "Тимофей", "Арина", "Руфина", "Илларион", "Стелла",
"Роман", "Валерия", "Игорь", "Алина", "Олег", "Диана", "Юрий", "Милана",
"Василий", "Ева", "Никита", "Алиса", "Константин", "Кира", "Денис", "Ангелина",
"Вячеслав", "Мирослава", "Григорий", "Эмилия", "Леонид", "Василиса", "Руслан",
"Стефания", "Арсений", "Есения", "Антон", "Яна", "Матвей", "Любовь", "Семен",
"Надежда", "Федор", "Софья", "Лев", "Варвара", "Егор", "Амелия", "Борис",
"Агата", "Захар", "Камилла", "Давид", "Олеся", "Ярослав", "Людмила", "Данила",
"Регина", "Марк", "Каролина", "Артур", "Нелли", "Глеб", "Инна", "Платон",
"Нина", "Святослав", "Римма", "Родион", "Лидия", "Эдуард", "Жанна", "Вадим",
"Рената", "Савелий", "Алла", "Назар", "Снежана", "Демид", "Лариса", "Филипп",
"Злата", "Тимур", "Майя", "Клим", "Эльвира", "Дамир", "Таисия", "Илья",
"Роза", "Виталий", "Азалия", "Степан", "Лиана", "Богдан", "Инесса", "Эрик",
"Ариана", "Алан", "Юлиана", "Лука", "Антонина", "Мирон", "Клавдия", "Гордей",
"Руслана", "Макар", "Елизавета", "Северин", "Александра", "Моисей", "Агафья",
"Наум", "Серафима", "Влад", "Фаина", "Кузьма", "Пелагея", "Ермак", "Ульяна",
"Тарас", "Марианна", "Остап", "Бронислава", "Архип", "Владислава", "Фома",
"Станислава", "Еремей", "Зинаида", "Прохор", "Раиса", "Мстислав", "Галина",
"Ростислав", "Валентина", "Серафим", "Евдокия", "Лаврентий", "Кристина",
"Никон", "Анфиса", "Феликс", "Лия", "Иннокентий", "Роксана", "Всеволод",
"Эвелина", "Модест", "Юнона", "Трофим", "Изабелла", "Аполлон", "Глория",
"Касьян", "Аврора", "Любомир", "Адель", "Бронислав", "Доминика", "Афанасий",
"Фрида", "Евстафий", "Ассоль", "Венедикт", "Цветана", "Епифан", "Мелисса",
"Добрыня"
)
_non_existent_names = [
"Ноль", "Целковый", "Полушка", "Четвертушка", "Осьмушка",
"Пудовичок", "Медячок", "Серебрячок", "Золотничок", "Девятичок"
]
assert set(names_pool).isdisjoint(set(_non_existent_names)), \
"В списке несуществующих имён существуют существующие имена сущностей"
names_pool_to_find = random.choices(names_pool, k=100) + _non_existent_names
def generate_phone(phone_len=11) -> str:
# 88005553535
return str(random.randint(10**phone_len, 10**(phone_len+1)-1))
def generate_test_data(N=10000, _sorted=False):
records = []
for i in range(N):
name = random.choice(names_pool)
phone = generate_phone()
records.append((name, phone))
if _sorted:
return sorted(records)
return records

View File

@ -0,0 +1,37 @@
import time
import random
from typing import Callable, Any
from task1.util.randomNames import names_pool_to_find, names_pool
def test(records: list,
insert_func: Callable[[Any, str, str], Any],
find_func: Callable[[Any, str], Any],
delete_func: Callable[[Any, str], Any]) -> dict:
data = None
# Вставка всех записей
start = time.perf_counter()
for item in records:
data = insert_func(data, item[0], item[1])
end = time.perf_counter()
insert_time = end - start
# Поиск 110 случайных записей
start = time.perf_counter()
for name in names_pool_to_find:
find_func(data, name)
end = time.perf_counter()
find_time = end - start
# Удаление 50 случайных записей
start = time.perf_counter()
for name in random.choices(names_pool, k = 50):
data = delete_func(data, name)
end = time.perf_counter()
delete_time = end - start
return {
"insert_time" : insert_time ,
"find_time" : find_time ,
"delete_time": delete_time
}