diff --git a/.gitignore b/.gitignore index 5d381cc..bdf8835 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ # C extensions *.so +.DS_Store # Distribution / packaging .Python build/ diff --git a/BudakovIS/428.md b/BudakovIS/428.md new file mode 100644 index 0000000..e69de29 diff --git a/DerbenevRY/428.md b/DerbenevRY/428.md new file mode 100644 index 0000000..e69de29 diff --git a/KuzminskiyAA/427.md b/KuzminskiyAA/427.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/KuzminskiyAA/427.md @@ -0,0 +1 @@ + diff --git a/KuznetsovAS/427.md b/KuznetsovAS/427.md new file mode 100644 index 0000000..e69de29 diff --git a/MashinDD/429.txt b/MashinDD/429.txt new file mode 100644 index 0000000..e69de29 diff --git a/MininaVD/427.txt b/MininaVD/427.txt new file mode 100644 index 0000000..ae507eb --- /dev/null +++ b/MininaVD/427.txt @@ -0,0 +1 @@ +427.txt diff --git a/MininaVD/MininaVD b/MininaVD/MininaVD new file mode 100644 index 0000000..ae507eb --- /dev/null +++ b/MininaVD/MininaVD @@ -0,0 +1 @@ +427.txt diff --git a/MusinAA/428b.md b/MusinAA/428b.md new file mode 100644 index 0000000..e69de29 diff --git a/MylnikovAS/427.md b/MylnikovAS/427.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/MylnikovAS/427.md @@ -0,0 +1 @@ + diff --git a/ProninVV/427.md b/ProninVV/427.md new file mode 100644 index 0000000..7d1a491 Binary files /dev/null and b/ProninVV/427.md differ diff --git a/ProninVV/file.txt b/ProninVV/file.txt new file mode 100644 index 0000000..7d1a491 Binary files /dev/null and b/ProninVV/file.txt differ diff --git a/README.md b/README.md index 7b46480..5952cd5 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ### Крайний срок приема работ 25.05.2026 до 14:00 -## Задание 1 -- репозиторий +## Задание 0 -- репозиторий [отдельный срок на создание PR с папкой: 28.02.2026] 0. Создай пользователя (логин — фамилия+инициалы слитно транслитом, как в терминал-классе). @@ -43,12 +43,157 @@ 6. Отправь ветку **в свой форк** на Gitea: ```bash - git push origin IvanovII + git push origin ``` + +если просит, перед этим сделать git push --set-upstream origin 7. **Создай запрос на слияние (Pull Request):** На Gitea перейди в свой форк, выбери ветку `IvanovII`, нажмите **Запрос на слияние**. Убедитесь, что: - Базовый репозиторий: **учебный** (преподавателя) - Базовая ветка: **develop** - Сравниваемая ветка: **свой форк / IvanovII** -8. Отправь PR. \ No newline at end of file +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) на отсортированных данных). + +- Почему хеш-таблица почти не чувствительна к порядку. + +- Почему связный список всегда медленен при поиске. + +- Как удаление работает в каждой структуре. + +* Вывод должен содержать ответ на вопрос: какую структуру и для каких задач (частые вставки, частый поиск, необходимость получать данные в порядке) стоит выбирать в реальной жизни.* \ No newline at end of file diff --git a/SimonovaMS/428.md b/SimonovaMS/428.md new file mode 100644 index 0000000..e69de29 diff --git a/SimonovaMS/428.txt b/SimonovaMS/428.txt new file mode 100644 index 0000000..e69de29 diff --git a/SolovevDS/428b.md b/SolovevDS/428b.md new file mode 100644 index 0000000..e69de29 diff --git a/VaravinVV/428b b/VaravinVV/428b new file mode 100644 index 0000000..55f6d6f --- /dev/null +++ b/VaravinVV/428b @@ -0,0 +1 @@ +428b diff --git a/VolkovVA/428b.md b/VolkovVA/428b.md new file mode 100644 index 0000000..e69de29 diff --git a/YanyaevAA/428b.md b/YanyaevAA/428b.md new file mode 100644 index 0000000..e69de29 diff --git a/YaroslavtsevAS/428.md b/YaroslavtsevAS/428.md new file mode 100644 index 0000000..43d371a --- /dev/null +++ b/YaroslavtsevAS/428.md @@ -0,0 +1 @@ +428 diff --git a/ZelentsovAV/428b.md b/ZelentsovAV/428b.md new file mode 100644 index 0000000..e69de29 diff --git a/anikinvd/428.md b/anikinvd/428.md new file mode 100644 index 0000000..e69de29 diff --git a/chizhikovaSM/428.md b/chizhikovaSM/428.md new file mode 100644 index 0000000..e69de29 diff --git a/duznb/429.md.txt b/duznb/429.md.txt new file mode 100644 index 0000000..e69de29 diff --git a/filippovavm/427 b/filippovavm/427 new file mode 100644 index 0000000..d2a1e59 --- /dev/null +++ b/filippovavm/427 @@ -0,0 +1 @@ +427 diff --git a/fomichevks/426.md.txt b/fomichevks/426.md.txt new file mode 100644 index 0000000..e69de29 diff --git a/ivanchenkoam/427.txt b/ivanchenkoam/427.txt new file mode 100644 index 0000000..7e48d39 --- /dev/null +++ b/ivanchenkoam/427.txt @@ -0,0 +1 @@ +856 diff --git a/ivantsovma/428.txt b/ivantsovma/428.txt new file mode 100644 index 0000000..e69de29 diff --git a/kolesovve/427.md b/kolesovve/427.md new file mode 100644 index 0000000..e69de29 diff --git a/komissarovgo/427.md b/komissarovgo/427.md new file mode 100644 index 0000000..7d1a491 Binary files /dev/null and b/komissarovgo/427.md differ diff --git a/konnovaea/429 b/konnovaea/429 new file mode 100644 index 0000000..cb11fbd --- /dev/null +++ b/konnovaea/429 @@ -0,0 +1 @@ +429 diff --git a/kornevma/426.md b/kornevma/426.md new file mode 100644 index 0000000..e69de29 diff --git a/krasnovia/429.txt b/krasnovia/429.txt new file mode 100644 index 0000000..e69de29 diff --git a/lomakinae/426 b/lomakinae/426 new file mode 100644 index 0000000..e69de29 diff --git a/meosyam/428.md.txt b/meosyam/428.md.txt new file mode 100644 index 0000000..e69de29 diff --git a/morozovns/1.py b/morozovns/1.py new file mode 100644 index 0000000..e1b03d0 --- /dev/null +++ b/morozovns/1.py @@ -0,0 +1,2 @@ +print("Zadanie adin!!11!adin!11!") +print("patch") diff --git a/morozovns/429 b/morozovns/429 new file mode 100644 index 0000000..cb11fbd --- /dev/null +++ b/morozovns/429 @@ -0,0 +1 @@ +429 diff --git a/nehoroshevaa/428b.md b/nehoroshevaa/428b.md new file mode 100644 index 0000000..225a97e --- /dev/null +++ b/nehoroshevaa/428b.md @@ -0,0 +1 @@ +428b diff --git a/nikolaevda/427.md b/nikolaevda/427.md new file mode 100644 index 0000000..7d1a491 Binary files /dev/null and b/nikolaevda/427.md differ diff --git a/novikovsd/428 b/novikovsd/428 new file mode 100644 index 0000000..e69de29 diff --git a/osininyai/427.md b/osininyai/427.md new file mode 100644 index 0000000..e69de29 diff --git a/petryaninyas/426.md b/petryaninyas/426.md new file mode 100644 index 0000000..e69de29 diff --git a/petryaninyas/README.md b/petryaninyas/README.md new file mode 100644 index 0000000..7e15dad --- /dev/null +++ b/petryaninyas/README.md @@ -0,0 +1,18 @@ +# Задание 1 — структуры данных + +Процедурная реализация: +- linked_list.py +- hash_table.py +- bst.py + +Эксперименты и отчёты: +- experiments.py +- plot_results.py +- results.csv +- docs/report.md +- docs/data/*.png + +Запуск: +```bash +python main.py +``` diff --git a/petryaninyas/bst.py b/petryaninyas/bst.py new file mode 100644 index 0000000..221fd67 --- /dev/null +++ b/petryaninyas/bst.py @@ -0,0 +1,118 @@ +from typing import Any, Dict, List, Optional + + +Node = Dict[str, Any] + + +def _make_node(name: str, phone: str) -> Node: + return {"name": name, "phone": phone, "left": None, "right": None} + + +def bst_insert(root: Optional[Node], name: str, phone: str) -> Node: + new_node = _make_node(name, phone) + + if root is None: + return new_node + + current = root + parent = None + + while current is not None: + parent = current + if name < current["name"]: + current = current["left"] + elif name > current["name"]: + current = current["right"] + else: + current["phone"] = phone + return root + + if name < parent["name"]: + parent["left"] = new_node + else: + parent["right"] = new_node + + return root + + +def bst_find(root: Optional[Node], name: str) -> Optional[str]: + current = root + while current is not None: + if name < current["name"]: + current = current["left"] + elif name > current["name"]: + current = current["right"] + else: + return current["phone"] + return None + + +def _find_min_node(node: Node) -> Node: + current = node + while current["left"] is not None: + current = current["left"] + return current + + +def bst_delete(root: Optional[Node], name: str) -> Optional[Node]: + if root is None: + return None + + parent = None + current = root + + while current is not None and current["name"] != name: + parent = current + if name < current["name"]: + current = current["left"] + else: + current = current["right"] + + if current is None: + return root + + if current["left"] is None or current["right"] is None: + child = current["left"] if current["left"] is not None else current["right"] + + if parent is None: + return child + + if parent["left"] is current: + parent["left"] = child + else: + parent["right"] = child + return root + + succ_parent = current + successor = current["right"] + while successor["left"] is not None: + succ_parent = successor + successor = successor["left"] + + current["name"] = successor["name"] + current["phone"] = successor["phone"] + + successor_child = successor["right"] + if succ_parent["left"] is successor: + succ_parent["left"] = successor_child + else: + succ_parent["right"] = successor_child + + return root + + +def bst_list_all(root: Optional[Node]) -> List[Dict[str, str]]: + result: List[Dict[str, str]] = [] + stack: List[Node] = [] + current = root + + while current is not None or stack: + while current is not None: + stack.append(current) + current = current["left"] + + current = stack.pop() + result.append({"name": current["name"], "phone": current["phone"]}) + current = current["right"] + + return result diff --git a/petryaninyas/docs/data/delete.png b/petryaninyas/docs/data/delete.png new file mode 100644 index 0000000..1ec4942 Binary files /dev/null and b/petryaninyas/docs/data/delete.png differ diff --git a/petryaninyas/docs/data/find.png b/petryaninyas/docs/data/find.png new file mode 100644 index 0000000..2868e6c Binary files /dev/null and b/petryaninyas/docs/data/find.png differ diff --git a/petryaninyas/docs/data/insert.png b/petryaninyas/docs/data/insert.png new file mode 100644 index 0000000..5c0e4ff Binary files /dev/null and b/petryaninyas/docs/data/insert.png differ diff --git a/petryaninyas/docs/data/results.csv b/petryaninyas/docs/data/results.csv new file mode 100644 index 0000000..4ccfb69 --- /dev/null +++ b/petryaninyas/docs/data/results.csv @@ -0,0 +1,109 @@ +Структура,Режим,Операция,Замер,Время (сек) +LinkedList,случайный,insert,1,4.2622492010 +LinkedList,случайный,find,1,0.0314994130 +LinkedList,случайный,delete,1,0.0149069000 +LinkedList,случайный,insert,2,4.0154580330 +LinkedList,случайный,find,2,0.0393284500 +LinkedList,случайный,delete,2,0.0210732100 +LinkedList,случайный,insert,3,4.0436019780 +LinkedList,случайный,find,3,0.0344933660 +LinkedList,случайный,delete,3,0.0152639850 +LinkedList,случайный,insert,4,3.7182993220 +LinkedList,случайный,find,4,0.0327698850 +LinkedList,случайный,delete,4,0.0149959540 +LinkedList,случайный,insert,5,3.7082228200 +LinkedList,случайный,find,5,0.0303762490 +LinkedList,случайный,delete,5,0.0141406560 +LinkedList,случайный,insert,среднее,3.9495662708 +LinkedList,случайный,find,среднее,0.0336934726 +LinkedList,случайный,delete,среднее,0.0160761410 +HashTable,случайный,insert,1,0.2059865770 +HashTable,случайный,find,1,0.0014966100 +HashTable,случайный,delete,1,0.0006891700 +HashTable,случайный,insert,2,0.2024331460 +HashTable,случайный,find,2,0.0015934880 +HashTable,случайный,delete,2,0.0007212620 +HashTable,случайный,insert,3,0.2126128040 +HashTable,случайный,find,3,0.0016566220 +HashTable,случайный,delete,3,0.0008358420 +HashTable,случайный,insert,4,0.2157934910 +HashTable,случайный,find,4,0.0015542810 +HashTable,случайный,delete,4,0.0007269120 +HashTable,случайный,insert,5,0.2079924580 +HashTable,случайный,find,5,0.0013696990 +HashTable,случайный,delete,5,0.0006616050 +HashTable,случайный,insert,среднее,0.2089636952 +HashTable,случайный,find,среднее,0.0015341400 +HashTable,случайный,delete,среднее,0.0007269582 +BST,случайный,insert,1,0.0166981280 +BST,случайный,find,1,0.0001569360 +BST,случайный,delete,1,0.0000917280 +BST,случайный,insert,2,0.0184119040 +BST,случайный,find,2,0.0001517110 +BST,случайный,delete,2,0.0001163770 +BST,случайный,insert,3,0.0174662270 +BST,случайный,find,3,0.0001582930 +BST,случайный,delete,3,0.0000892660 +BST,случайный,insert,4,0.0191369100 +BST,случайный,find,4,0.0002087170 +BST,случайный,delete,4,0.0001067050 +BST,случайный,insert,5,0.0184276900 +BST,случайный,find,5,0.0002767720 +BST,случайный,delete,5,0.0001067660 +BST,случайный,insert,среднее,0.0180281718 +BST,случайный,find,среднее,0.0001904858 +BST,случайный,delete,среднее,0.0001021684 +LinkedList,отсортированный,insert,1,2.9875078340 +LinkedList,отсортированный,find,1,0.0237300610 +LinkedList,отсортированный,delete,1,0.0111698260 +LinkedList,отсортированный,insert,2,3.0573987940 +LinkedList,отсортированный,find,2,0.0243270360 +LinkedList,отсортированный,delete,2,0.0115366030 +LinkedList,отсортированный,insert,3,2.9641987260 +LinkedList,отсортированный,find,3,0.0236313330 +LinkedList,отсортированный,delete,3,0.0112848510 +LinkedList,отсортированный,insert,4,3.0345914950 +LinkedList,отсортированный,find,4,0.0240271220 +LinkedList,отсортированный,delete,4,0.0112117310 +LinkedList,отсортированный,insert,5,2.9481954700 +LinkedList,отсортированный,find,5,0.0239006100 +LinkedList,отсортированный,delete,5,0.0110857710 +LinkedList,отсортированный,insert,среднее,2.9983784638 +LinkedList,отсортированный,find,среднее,0.0239232324 +LinkedList,отсортированный,delete,среднее,0.0112577564 +HashTable,отсортированный,insert,1,0.1997087560 +HashTable,отсортированный,find,1,0.0017550400 +HashTable,отсортированный,delete,1,0.0008407980 +HashTable,отсортированный,insert,2,0.1968675190 +HashTable,отсортированный,find,2,0.0019886760 +HashTable,отсортированный,delete,2,0.0008920910 +HashTable,отсортированный,insert,3,0.1907563580 +HashTable,отсортированный,find,3,0.0018447440 +HashTable,отсортированный,delete,3,0.0008684640 +HashTable,отсортированный,insert,4,0.2625327630 +HashTable,отсортированный,find,4,0.0016053140 +HashTable,отсортированный,delete,4,0.0008098670 +HashTable,отсортированный,insert,5,0.1936840590 +HashTable,отсортированный,find,5,0.0019015160 +HashTable,отсортированный,delete,5,0.0009053780 +HashTable,отсортированный,insert,среднее,0.2087098910 +HashTable,отсортированный,find,среднее,0.0018190580 +HashTable,отсортированный,delete,среднее,0.0008633196 +BST,отсортированный,insert,1,4.2195800190 +BST,отсортированный,find,1,0.0389314570 +BST,отсортированный,delete,1,0.0190308920 +BST,отсортированный,insert,2,4.1356184250 +BST,отсортированный,find,2,0.0383339310 +BST,отсортированный,delete,2,0.0194247740 +BST,отсортированный,insert,3,4.1204731890 +BST,отсортированный,find,3,0.0388593320 +BST,отсортированный,delete,3,0.0215428460 +BST,отсортированный,insert,4,4.2120902370 +BST,отсортированный,find,4,0.0378190250 +BST,отсортированный,delete,4,0.0188528460 +BST,отсортированный,insert,5,4.1304951260 +BST,отсортированный,find,5,0.0359927840 +BST,отсортированный,delete,5,0.0179617110 +BST,отсортированный,insert,среднее,4.1636513992 +BST,отсортированный,find,среднее,0.0379873058 +BST,отсортированный,delete,среднее,0.0193626138 diff --git a/petryaninyas/docs/Отчёт по работе.docx b/petryaninyas/docs/Отчёт по работе.docx new file mode 100644 index 0000000..d9863d0 Binary files /dev/null and b/petryaninyas/docs/Отчёт по работе.docx differ diff --git a/petryaninyas/experiments.py b/petryaninyas/experiments.py new file mode 100644 index 0000000..f5face1 --- /dev/null +++ b/petryaninyas/experiments.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +import csv +import random +import time +from pathlib import Path +from typing import Dict, List, Tuple + +from linked_list import ll_insert, ll_find, ll_delete +from hash_table import ht_insert, ht_find, ht_delete +from bst import bst_insert, bst_find, bst_delete +from utils import generate_records, prepare_records_variants + + +Record = Tuple[str, str] + + +def make_missing_names(count: int = 10) -> List[str]: + return [f"None_{i}" for i in range(count)] + + +def pick_existing_names(records: List[Record], count: int, seed: int = 42) -> List[str]: + rng = random.Random(seed) + unique_names = list(dict.fromkeys(name for name, _ in records)) + if len(unique_names) < count: + raise ValueError(f"Not enough unique names: need {count}, got {len(unique_names)}") + return rng.sample(unique_names, count) + + +def pick_delete_names(records: List[Record], count: int = 50, seed: int = 43) -> List[str]: + rng = random.Random(seed) + unique_names = list(dict.fromkeys(name for name, _ in records)) + if len(unique_names) < count: + raise ValueError(f"Not enough unique names: need {count}, got {len(unique_names)}") + return rng.sample(unique_names, count) + + +def build_structure(structure_name: str, records: List[Record], buckets_count: int = 2048): + if structure_name == "linked_list": + structure = None + for name, phone in records: + structure = ll_insert(structure, name, phone) + return structure + + if structure_name == "hash_table": + buckets = [None] * buckets_count + for name, phone in records: + buckets = ht_insert(buckets, name, phone) + return buckets + + if structure_name == "bst": + root = None + for name, phone in records: + root = bst_insert(root, name, phone) + return root + + raise ValueError(f"Unknown structure: {structure_name}") + + +def do_find(structure_name: str, structure: object, existing_names: List[str], missing_names: List[str]) -> None: + if structure_name == "linked_list": + for name in existing_names: + ll_find(structure, name) + for name in missing_names: + ll_find(structure, name) + return + + if structure_name == "hash_table": + for name in existing_names: + ht_find(structure, name) + for name in missing_names: + ht_find(structure, name) + return + + if structure_name == "bst": + for name in existing_names: + bst_find(structure, name) + for name in missing_names: + bst_find(structure, name) + return + + raise ValueError(f"Unknown structure: {structure_name}") + + +def do_delete(structure_name: str, structure: object, delete_names: List[str]): + if structure_name == "linked_list": + for name in delete_names: + structure = ll_delete(structure, name) + return structure + + if structure_name == "hash_table": + for name in delete_names: + structure = ht_delete(structure, name) + return structure + + if structure_name == "bst": + for name in delete_names: + structure = bst_delete(structure, name) + return structure + + raise ValueError(f"Unknown structure: {structure_name}") + + +def measure_once(structure_name: str, records: List[Record], buckets_count: int = 2048) -> Dict[str, float]: + existing_names = pick_existing_names(records, 100, seed=42) + missing_names = make_missing_names(10) + delete_names = pick_delete_names(records, 50, seed=43) + + start = time.perf_counter() + structure = build_structure(structure_name, records, buckets_count=buckets_count) + insert_time = time.perf_counter() - start + + start = time.perf_counter() + do_find(structure_name, structure, existing_names, missing_names) + find_time = time.perf_counter() - start + + start = time.perf_counter() + structure = do_delete(structure_name, structure, delete_names) + delete_time = time.perf_counter() - start + + return {"insert": insert_time, "find": find_time, "delete": delete_time} + + +def run_experiments(n: int = 10000, buckets_count: int = 2048, repeats: int = 5): + records = generate_records(n, repeat_names=False) + records_shuffled, records_sorted = prepare_records_variants(records) + + datasets = [ + ("случайный", records_shuffled), + ("отсортированный", records_sorted), + ] + structures = [ + ("LinkedList", "linked_list"), + ("HashTable", "hash_table"), + ("BST", "bst"), + ] + operations = ("insert", "find", "delete") + + rows = [["Структура", "Режим", "Операция", "Замер", "Время (сек)"]] + + for mode_name, dataset_records in datasets: + for human_name, structure_name in structures: + times_by_op = {op: [] for op in operations} + + for attempt in range(1, repeats + 1): + result = measure_once(structure_name, dataset_records, buckets_count=buckets_count) + for op_name in operations: + elapsed = result[op_name] + times_by_op[op_name].append(elapsed) + rows.append([human_name, mode_name, op_name, attempt, f"{elapsed:.10f}"]) + + for op_name in operations: + avg_time = sum(times_by_op[op_name]) / len(times_by_op[op_name]) + rows.append([human_name, mode_name, op_name, "среднее", f"{avg_time:.10f}"]) + + return rows + + +def save_results_csv(rows, filename: str = "results.csv"): + with open(filename, "w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerows(rows) + + +def main(): + rows = run_experiments(n=10000, buckets_count=2048, repeats=5) + save_results_csv(rows, "results.csv") + print("Saved results.csv") + + +if __name__ == "__main__": + main() diff --git a/petryaninyas/hash_table.py b/petryaninyas/hash_table.py new file mode 100644 index 0000000..9aa720d --- /dev/null +++ b/petryaninyas/hash_table.py @@ -0,0 +1,44 @@ + + +from typing import Any, Dict, List, Optional + +from linked_list import ll_insert, ll_find, ll_delete, ll_list_all + + +Bucket = Optional[Dict[str, Any]] + + +def _hash_name(name: str, buckets_count: int) -> int: + if buckets_count <= 0: + return 0 + return sum(ord(ch) for ch in name) % buckets_count + + +def ht_insert(buckets: List[Bucket], name: str, phone: str) -> List[Bucket]: + if not buckets: + return buckets + index = _hash_name(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + return buckets + + +def ht_find(buckets: List[Bucket], name: str) -> Optional[str]: + if not buckets: + return None + index = _hash_name(name, len(buckets)) + return ll_find(buckets[index], name) + + +def ht_delete(buckets: List[Bucket], name: str) -> List[Bucket]: + if not buckets: + return buckets + index = _hash_name(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + return buckets + + +def ht_list_all(buckets: List[Bucket]) -> List[Dict[str, str]]: + records: List[Dict[str, str]] = [] + for head in buckets: + records.extend(ll_list_all(head)) + return sorted(records, key=lambda x: x["name"]) diff --git a/petryaninyas/linked_list.py b/petryaninyas/linked_list.py new file mode 100644 index 0000000..0260036 --- /dev/null +++ b/petryaninyas/linked_list.py @@ -0,0 +1,73 @@ + + +from typing import Any, Dict, List, Optional + + +Node = Dict[str, Any] + + +def _make_node(name: str, phone: str) -> Node: + return {"name": name, "phone": phone, "next": None} + + +def sort_records(records: List[Dict[str, str]]) -> List[Dict[str, str]]: + + return sorted(records, key=lambda x: x["name"]) + + +def ll_insert(head: Optional[Node], name: str, phone: str) -> Node: + + new_node = _make_node(name, phone) + + if head is None: + return new_node + + current = head + while current is not None: + if current["name"] == name: + current["phone"] = phone + return head + if current["next"] is None: + current["next"] = new_node + return head + current = current["next"] + + return head + + +def ll_find(head: Optional[Node], name: str) -> Optional[str]: + current = head + while current is not None: + if current["name"] == name: + return current["phone"] + current = current["next"] + return None + + +def ll_delete(head: Optional[Node], name: str) -> Optional[Node]: + if head is None: + return None + + if head["name"] == name: + return head["next"] + + prev = head + current = head["next"] + + while current is not None: + if current["name"] == name: + prev["next"] = current["next"] + return head + prev = current + current = current["next"] + + return head + + +def ll_list_all(head: Optional[Node]) -> List[Dict[str, str]]: + records: List[Dict[str, str]] = [] + current = head + while current is not None: + records.append({"name": current["name"], "phone": current["phone"]}) + current = current["next"] + return sort_records(records) diff --git a/petryaninyas/main.py b/petryaninyas/main.py new file mode 100644 index 0000000..70de618 --- /dev/null +++ b/petryaninyas/main.py @@ -0,0 +1,21 @@ + + +from __future__ import annotations + +import csv +from pathlib import Path + +from experiments import run_experiments, save_results_csv +from plot_results import build_graphs, load_average_results + + +def main(): + rows = run_experiments(n=10000, buckets_count=2048, repeats=5) + save_results_csv(rows, "results.csv") + averaged = load_average_results("results.csv") + build_graphs(averaged, output_dir="docs/data") + print("Done.") + + +if __name__ == "__main__": + main() diff --git a/petryaninyas/plot_results.py b/petryaninyas/plot_results.py new file mode 100644 index 0000000..f6b9d3a --- /dev/null +++ b/petryaninyas/plot_results.py @@ -0,0 +1,66 @@ +from __future__ import annotations +import csv +from collections import defaultdict +from pathlib import Path +import matplotlib.pyplot as plt +from typing import List, Dict, Any + +# Карта соответствия заголовков CSV для удобства поддержки +_CSV_KEYS = { + "STRUCTURE": "Структура", + "MODE": "Режим", + "OP": "Операция", + "MEASURE": "Замер", + "TIME": "Время (сек)" +} + +def load_average_results(csv_file: str) -> List[Dict[str, Any]]: + """Загружает только строки со средними значениями из CSV.""" + with open(csv_file, "r", encoding="utf-8") as fh: + return [ + { + "structure": row[_CSV_KEYS["STRUCTURE"]], + "mode": row[_CSV_KEYS["MODE"]], + "operation": row[_CSV_KEYS["OP"]], + "time": float(row[_CSV_KEYS["TIME"]]) + } + for row in csv.DictReader(fh) + if row[_CSV_KEYS["MEASURE"]] == "среднее" + ] + +def _render_chart(operation: str, data: List[Dict[str, Any]], save_path: Path) -> None: + """Вспомогательная функция для отрисовки одного столбчатого графика.""" + fig, ax = plt.subplots(figsize=(10, 5)) + labels = [f"{rec['structure']}\n{rec['mode']}" for rec in data] + values = [rec["time"] for rec in data] + + ax.bar(labels, values, color="cornflowerblue", edgecolor="navy") + ax.set_title(f"{operation.capitalize()} Performance Comparison") + ax.set_xlabel("Data Structure & Input Order") + ax.set_ylabel("Execution Time (seconds)") + ax.tick_params(axis="x", rotation=15) + fig.tight_layout() + fig.savefig(save_path, dpi=150) + plt.close(fig) + print(f"Saved chart: {save_path}") + +def build_graphs(results: List[Dict[str, Any]], output_dir: str = "docs/data") -> None: + """Группирует результаты по операциям и сохраняет графики.""" + out_path = Path(output_dir) + out_path.mkdir(parents=True, exist_ok=True) + + grouped = defaultdict(list) + for record in results: + grouped[record["operation"]].append(record) + + for op_name in ("insert", "find", "delete"): + if op_name in grouped: + target_file = out_path / f"{op_name}.png" + _render_chart(op_name, grouped[op_name], target_file) + +def main(): + avg_data = load_average_results("results.csv") + build_graphs(avg_data) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/petryaninyas/requirements.txt b/petryaninyas/requirements.txt new file mode 100644 index 0000000..a9006fd --- /dev/null +++ b/petryaninyas/requirements.txt @@ -0,0 +1 @@ +matplotlib>=3.8 diff --git a/petryaninyas/results.csv b/petryaninyas/results.csv new file mode 100644 index 0000000..4624802 --- /dev/null +++ b/petryaninyas/results.csv @@ -0,0 +1,109 @@ +Структура,Режим,Операция,Замер,Время (сек) +LinkedList,случайный,insert,1,6.8132659000 +LinkedList,случайный,find,1,0.0553455000 +LinkedList,случайный,delete,1,0.0304272000 +LinkedList,случайный,insert,2,6.8263299000 +LinkedList,случайный,find,2,0.0562645000 +LinkedList,случайный,delete,2,0.0303858000 +LinkedList,случайный,insert,3,6.8117682000 +LinkedList,случайный,find,3,0.0567219000 +LinkedList,случайный,delete,3,0.0302671000 +LinkedList,случайный,insert,4,7.0393228000 +LinkedList,случайный,find,4,0.0544359000 +LinkedList,случайный,delete,4,0.0313359000 +LinkedList,случайный,insert,5,7.0633509000 +LinkedList,случайный,find,5,0.0552176000 +LinkedList,случайный,delete,5,0.0318664000 +LinkedList,случайный,insert,среднее,6.9108075400 +LinkedList,случайный,find,среднее,0.0555970800 +LinkedList,случайный,delete,среднее,0.0308564800 +HashTable,случайный,insert,1,0.3898307000 +HashTable,случайный,find,1,0.0022226000 +HashTable,случайный,delete,1,0.0012723000 +HashTable,случайный,insert,2,0.3844561000 +HashTable,случайный,find,2,0.0024825000 +HashTable,случайный,delete,2,0.0013425000 +HashTable,случайный,insert,3,0.3740853000 +HashTable,случайный,find,3,0.0026141000 +HashTable,случайный,delete,3,0.0013710000 +HashTable,случайный,insert,4,0.3870099000 +HashTable,случайный,find,4,0.0022063000 +HashTable,случайный,delete,4,0.0015940000 +HashTable,случайный,insert,5,0.3869087000 +HashTable,случайный,find,5,0.0022110000 +HashTable,случайный,delete,5,0.0012524000 +HashTable,случайный,insert,среднее,0.3844581400 +HashTable,случайный,find,среднее,0.0023473000 +HashTable,случайный,delete,среднее,0.0013664400 +BST,случайный,insert,1,0.0375842000 +BST,случайный,find,1,0.0003637000 +BST,случайный,delete,1,0.0001893000 +BST,случайный,insert,2,0.0364385000 +BST,случайный,find,2,0.0003284000 +BST,случайный,delete,2,0.0002408000 +BST,случайный,insert,3,0.0359604000 +BST,случайный,find,3,0.0003705000 +BST,случайный,delete,3,0.0001883000 +BST,случайный,insert,4,0.0371920000 +BST,случайный,find,4,0.0003347000 +BST,случайный,delete,4,0.0001711000 +BST,случайный,insert,5,0.0385580000 +BST,случайный,find,5,0.0003628000 +BST,случайный,delete,5,0.0001828000 +BST,случайный,insert,среднее,0.0371466200 +BST,случайный,find,среднее,0.0003520200 +BST,случайный,delete,среднее,0.0001944600 +LinkedList,отсортированный,insert,1,6.7166487000 +LinkedList,отсортированный,find,1,0.0614362000 +LinkedList,отсортированный,delete,1,0.0324487000 +LinkedList,отсортированный,insert,2,6.8582294000 +LinkedList,отсортированный,find,2,0.0588546000 +LinkedList,отсортированный,delete,2,0.0332572000 +LinkedList,отсортированный,insert,3,6.6991410000 +LinkedList,отсортированный,find,3,0.0521304000 +LinkedList,отсортированный,delete,3,0.0296734000 +LinkedList,отсортированный,insert,4,6.7166336000 +LinkedList,отсортированный,find,4,0.0521848000 +LinkedList,отсортированный,delete,4,0.0286064000 +LinkedList,отсортированный,insert,5,6.6510829000 +LinkedList,отсортированный,find,5,0.0547075000 +LinkedList,отсортированный,delete,5,0.0285625000 +LinkedList,отсортированный,insert,среднее,6.7283471200 +LinkedList,отсортированный,find,среднее,0.0558627000 +LinkedList,отсортированный,delete,среднее,0.0305096400 +HashTable,отсортированный,insert,1,0.3645209000 +HashTable,отсортированный,find,1,0.0032770000 +HashTable,отсортированный,delete,1,0.0018631000 +HashTable,отсортированный,insert,2,0.3579217000 +HashTable,отсортированный,find,2,0.0032143000 +HashTable,отсортированный,delete,2,0.0016427000 +HashTable,отсортированный,insert,3,0.3684879000 +HashTable,отсортированный,find,3,0.0026412000 +HashTable,отсортированный,delete,3,0.0014870000 +HashTable,отсортированный,insert,4,0.3873619000 +HashTable,отсортированный,find,4,0.0028024000 +HashTable,отсортированный,delete,4,0.0015786000 +HashTable,отсортированный,insert,5,0.3642992000 +HashTable,отсортированный,find,5,0.0031264000 +HashTable,отсортированный,delete,5,0.0015886000 +HashTable,отсортированный,insert,среднее,0.3685183200 +HashTable,отсортированный,find,среднее,0.0030122600 +HashTable,отсортированный,delete,среднее,0.0016320000 +BST,отсортированный,insert,1,10.5552378000 +BST,отсортированный,find,1,0.1016856000 +BST,отсортированный,delete,1,0.0422728000 +BST,отсортированный,insert,2,10.3035871000 +BST,отсортированный,find,2,0.1008642000 +BST,отсортированный,delete,2,0.0450330000 +BST,отсортированный,insert,3,10.6304005000 +BST,отсортированный,find,3,0.1073470000 +BST,отсортированный,delete,3,0.0816121000 +BST,отсортированный,insert,4,10.3183078000 +BST,отсортированный,find,4,0.1005074000 +BST,отсортированный,delete,4,0.0422195000 +BST,отсортированный,insert,5,10.3131368000 +BST,отсортированный,find,5,0.1001096000 +BST,отсортированный,delete,5,0.0416660000 +BST,отсортированный,insert,среднее,10.4241340000 +BST,отсортированный,find,среднее,0.1021027600 +BST,отсортированный,delete,среднее,0.0505606800 diff --git a/petryaninyas/utils.py b/petryaninyas/utils.py new file mode 100644 index 0000000..2befe4b --- /dev/null +++ b/petryaninyas/utils.py @@ -0,0 +1,29 @@ +import random +from typing import List, Tuple + +Record = Tuple[str, str] + +NAME_POOL = ( + "User_Alex", "User_Bob", "User_Cat", "User_Dan", "User_Eva", + "User_Fox", "User_Geo", "User_Hen", "User_Ira", "User_Leo" +) + +def generate_records(n: int, repeat_names: bool = False, seed: int = 42) -> List[Record]: + """Генерирует n кортежей (имя, телефон).""" + rng = random.Random(seed) + if repeat_names: + return [ + (rng.choice(NAME_POOL), str(rng.randint(10**9, 10**10 - 1))) + for _ in range(n) + ] + return [ + (f"User_{i:05d}", str(10**9 + i)) + for i in range(n) + ] + +def prepare_records_variants(records: List[Record], seed: int = 42) -> Tuple[List[Record], List[Record]]: + """Возвращает пару: (перемешанный список, отсортированный по имени список).""" + shuffled = records.copy() + random.Random(seed).shuffle(shuffled) + sorted_records = sorted(records, key=lambda rec: rec[0]) + return shuffled, sorted_records \ No newline at end of file diff --git a/pogodinda/427.md.txt b/pogodinda/427.md.txt new file mode 100644 index 0000000..e69de29 diff --git a/pomelovsd/427 b/pomelovsd/427 new file mode 100644 index 0000000..acf052b --- /dev/null +++ b/pomelovsd/427 @@ -0,0 +1 @@ +427 diff --git a/raskatovia/429.md b/raskatovia/429.md new file mode 100644 index 0000000..a90fd09 Binary files /dev/null and b/raskatovia/429.md differ diff --git a/romanovpv/427.md b/romanovpv/427.md new file mode 100644 index 0000000..e69de29 diff --git a/semyanovra/426.md b/semyanovra/426.md new file mode 100644 index 0000000..e69de29 diff --git a/shahovaa/429.md b/shahovaa/429.md new file mode 100644 index 0000000..e69de29 diff --git a/shalovsa/429.txt b/shalovsa/429.txt new file mode 100644 index 0000000..e69de29 diff --git a/shekurovaa/429.md b/shekurovaa/429.md new file mode 100644 index 0000000..e69de29 diff --git a/skorohodovsa/427 b/skorohodovsa/427 new file mode 100644 index 0000000..acf052b --- /dev/null +++ b/skorohodovsa/427 @@ -0,0 +1 @@ +427 diff --git a/skorohodovsa/main.py b/skorohodovsa/main.py new file mode 100644 index 0000000..ba8db40 --- /dev/null +++ b/skorohodovsa/main.py @@ -0,0 +1,4 @@ +from math import sin + +for i in range(10000): + print(" " * round(50 * (1 + sin(i/100))), "Hello World!") diff --git a/smirnovad/429.md b/smirnovad/429.md new file mode 100644 index 0000000..e69de29 diff --git a/sobininaas/429.rtf b/sobininaas/429.rtf new file mode 100644 index 0000000..02f05c1 --- /dev/null +++ b/sobininaas/429.rtf @@ -0,0 +1,6 @@ +{\rtf1\ansi\ansicpg1251\cocoartf2761 +\cocoatextscaling0\cocoaplatform0{\fonttbl} +{\colortbl;\red255\green255\blue255;} +{\*\expandedcolortbl;;} +\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0 +} \ No newline at end of file diff --git a/soldatkinao/428б.md b/soldatkinao/428б.md new file mode 100644 index 0000000..cf53638 --- /dev/null +++ b/soldatkinao/428б.md @@ -0,0 +1 @@ + 뢮 ࠭ (ECHO) 祭. diff --git a/sorokinfi/427.md b/sorokinfi/427.md new file mode 100644 index 0000000..e69de29 diff --git a/stepinim/428.md b/stepinim/428.md new file mode 100644 index 0000000..e69de29 diff --git a/stepushovgs/427 b/stepushovgs/427 new file mode 100644 index 0000000..d2a1e59 --- /dev/null +++ b/stepushovgs/427 @@ -0,0 +1 @@ +427 diff --git a/svetlakovkyu/426 b/svetlakovkyu/426 new file mode 100644 index 0000000..e69de29 diff --git a/talantsevgi/427.txt b/talantsevgi/427.txt new file mode 100644 index 0000000..b610fc3 --- /dev/null +++ b/talantsevgi/427.txt @@ -0,0 +1 @@ +732489234 diff --git a/tseremonnikovaaa/427 b/tseremonnikovaaa/427 new file mode 100644 index 0000000..acf052b --- /dev/null +++ b/tseremonnikovaaa/427 @@ -0,0 +1 @@ +427 diff --git a/volkovim/428b.md b/volkovim/428b.md new file mode 100644 index 0000000..225a97e --- /dev/null +++ b/volkovim/428b.md @@ -0,0 +1 @@ +428b diff --git a/zaharoves/429.md b/zaharoves/429.md new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/zaharoves/429.md @@ -0,0 +1 @@ + diff --git a/zverevem/429.txt b/zverevem/429.txt new file mode 100644 index 0000000..e69de29