forked from UNN/2026-rff_mp
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7c181f30b | ||
|
|
b002e85958 | ||
|
|
94a9cba182 | ||
|
|
cb02fb20fe | ||
|
|
94bc3b8524 | ||
|
|
7026ad395d | ||
|
|
dd4eca8407 | ||
|
|
b383ed1cdc | ||
|
|
9bdefcc922 | ||
|
|
0b092e8d26 | ||
|
|
b984ec3569 | ||
|
|
9c1cd94e87 | ||
|
|
8124c755f8 |
2
MusinAA/.gitignore
vendored
Normal file
2
MusinAA/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.vscode/
|
||||||
|
*/tests/
|
||||||
148
MusinAA/docs/Report 1.ipynb
Normal file
148
MusinAA/docs/Report 1.ipynb
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "2acfa743",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# 0. Подготовим окружение"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 26,
|
||||||
|
"id": "4689b73e",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import sys\n",
|
||||||
|
"import os\n",
|
||||||
|
"sys.path.insert(0, os.path.abspath( '../task1'))\n",
|
||||||
|
"sys.path.insert(0, os.path.abspath( '../'))"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "37cc11a5",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# 1. Генерация тестовых данных\n",
|
||||||
|
"\n",
|
||||||
|
"Создадим список records из N=10000 элементов. Каждый элемент — кортеж (name, phone). \n",
|
||||||
|
"Имена возъмём случайные из небольшого набора (чтобы были повторения и коллизии). \n",
|
||||||
|
"Для проверки влияния порядка подготовим два варианта: \n",
|
||||||
|
"\n",
|
||||||
|
"_records_shuffled_ — случайный порядок. \n",
|
||||||
|
"_records_sorted_ — отсортированный по имени (по алфавиту)."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 27,
|
||||||
|
"id": "a3b5c31b",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"from util.randomNames import generate_test_data\n",
|
||||||
|
"from util.timeTester import test\n",
|
||||||
|
"\n",
|
||||||
|
"records_shuffled = generate_test_data(N=10000)\n",
|
||||||
|
"records_sorted = generate_test_data(N=10000, _sorted=True)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "markdown",
|
||||||
|
"id": "c2f4989c",
|
||||||
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"# 2. Проведение замеров"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 28,
|
||||||
|
"id": "df12d41d",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Подготовим функции СД, которые будем тестировать\n",
|
||||||
|
"from structures.LinkedList import *\n",
|
||||||
|
"from structures.HashTable import *\n",
|
||||||
|
"from structures.BinaryTree import *\n",
|
||||||
|
"\n",
|
||||||
|
"func_list = {\"Связанный список\" : (ll_insert, ll_find, ll_delete),\n",
|
||||||
|
" \"Хэш-таблица\" : (ht_insert, ht_find, ht_delete),\n",
|
||||||
|
" \"Бинарное дерево\" : (bst_insert, bst_find, bst_delete)}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 29,
|
||||||
|
"id": "cc8d0436",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Проведём замеры\n",
|
||||||
|
"report = [[\"Структура\", \"Режим\", \"Вставка\", \"Поиск\", \"Удаление\"]]\n",
|
||||||
|
"records = {\"Cлучайный\" : records_shuffled, \"Отсортированный\" : records_sorted}\n",
|
||||||
|
"\n",
|
||||||
|
"TEST_ITERATIONS_NUM = 5\n",
|
||||||
|
"\n",
|
||||||
|
"for _ in range(TEST_ITERATIONS_NUM):\n",
|
||||||
|
" for mode, data in records.items():\n",
|
||||||
|
" for struct_name, fns in func_list.items():\n",
|
||||||
|
" result = test(data, *fns)\n",
|
||||||
|
" row = [struct_name, mode,\n",
|
||||||
|
" result[\"insert_time\"],\n",
|
||||||
|
" result[\"find_time\"],\n",
|
||||||
|
" result[\"delete_time\"]]\n",
|
||||||
|
" report.append(row)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 30,
|
||||||
|
"id": "2eedf056",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"# Сохраним данные в csv\n",
|
||||||
|
"import csv\n",
|
||||||
|
"with open(\"data/results.csv\", \"w\", newline=\"\") as f:\n",
|
||||||
|
" writer = csv.writer(f)\n",
|
||||||
|
" writer.writerows(report)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "8523ae48",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"TODO проверить работает ли оно вообще"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.14.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
31
MusinAA/docs/data/results.csv
Normal file
31
MusinAA/docs/data/results.csv
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
Структура,Режим,Вставка,Поиск,Удаление
|
||||||
|
Связанный список,Cлучайный,4.661078881000321,0.012941928999680385,0.020117076999667916
|
||||||
|
Хэш-таблица,Cлучайный,0.18879180299973086,0.0014481220005109208,0.0012560499999381136
|
||||||
|
Бинарное дерево,Cлучайный,0.01616830700004357,0.00016560199946979992,0.00016564300040045055
|
||||||
|
Связанный список,Отсортированный,5.038275819999399,0.07141196100019442,0.1353478549999636
|
||||||
|
Хэш-таблица,Отсортированный,0.1631836659998953,0.003129734999674838,0.003202291999514273
|
||||||
|
Бинарное дерево,Отсортированный,0.16634428900033527,0.0017354540004816954,0.001874036000117485
|
||||||
|
Связанный список,Cлучайный,4.664316974999565,0.014653709000413073,0.02280332600003021
|
||||||
|
Хэш-таблица,Cлучайный,0.186777711999639,0.0010283499996148748,0.0009878339997158037
|
||||||
|
Бинарное дерево,Cлучайный,0.016256722999969497,0.00015175599946815055,0.0001649409996389295
|
||||||
|
Связанный список,Отсортированный,5.907060995999927,0.08458327099924645,0.12409427100010362
|
||||||
|
Хэш-таблица,Отсортированный,0.18781050100005814,0.003518858999996155,0.0038270310005827923
|
||||||
|
Бинарное дерево,Отсортированный,0.17500397699950554,0.0019018089997189236,0.001833940000324219
|
||||||
|
Связанный список,Cлучайный,5.755159846999959,0.016742109000006167,0.026386416000605095
|
||||||
|
Хэш-таблица,Cлучайный,0.22951744300007704,0.0009921219998432207,0.0011742059996322496
|
||||||
|
Бинарное дерево,Cлучайный,0.01580532200023299,0.00017768500038073398,0.00019722199976968113
|
||||||
|
Связанный список,Отсортированный,6.30150953500015,0.11176149099992472,0.1474957850005012
|
||||||
|
Хэш-таблица,Отсортированный,0.18557919999966543,0.003155624000100943,0.002713390000280924
|
||||||
|
Бинарное дерево,Отсортированный,0.19082545899982506,0.001985636999961571,0.002389950000178942
|
||||||
|
Связанный список,Cлучайный,6.395210606000546,0.014804241999627266,0.02304338900012226
|
||||||
|
Хэш-таблица,Cлучайный,0.1902105980007036,0.000852088000101503,0.0009626670007492066
|
||||||
|
Бинарное дерево,Cлучайный,0.01663886400001502,0.00016400000004068715,0.000184548000106588
|
||||||
|
Связанный список,Отсортированный,4.850914527999521,0.0771323629996914,0.1152741280002374
|
||||||
|
Хэш-таблица,Отсортированный,0.17607759100064868,0.002924628000073426,0.0033850670006358996
|
||||||
|
Бинарное дерево,Отсортированный,0.19345043999965128,0.0018585970001367969,0.0019752460002564476
|
||||||
|
Связанный список,Cлучайный,4.803303787999539,0.015972447000422108,0.0223228390004806
|
||||||
|
Хэш-таблица,Cлучайный,0.19020581800032232,0.0011616620004133438,0.0009839170006671338
|
||||||
|
Бинарное дерево,Cлучайный,0.016469425000650517,0.000160212000082538,0.00017693399968266021
|
||||||
|
Связанный список,Отсортированный,4.741838529000233,0.075463203000254,0.10462550600004761
|
||||||
|
Хэш-таблица,Отсортированный,0.16722737300005974,0.004002030999799899,0.005207103999964602
|
||||||
|
Бинарное дерево,Отсортированный,0.16575109800032806,0.0020921670002280734,0.002277146999404067
|
||||||
|
0
MusinAA/task1/__init__.py
Normal file
0
MusinAA/task1/__init__.py
Normal file
88
MusinAA/task1/structures/BinaryTree.py
Normal file
88
MusinAA/task1/structures/BinaryTree.py
Normal 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'])
|
||||||
59
MusinAA/task1/structures/HashTable.py
Normal file
59
MusinAA/task1/structures/HashTable.py
Normal 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])
|
||||||
60
MusinAA/task1/structures/LinkedList.py
Normal file
60
MusinAA/task1/structures/LinkedList.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""
|
||||||
|
Связный список (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:
|
||||||
|
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
|
||||||
0
MusinAA/task1/structures/__init__.py
Normal file
0
MusinAA/task1/structures/__init__.py
Normal file
0
MusinAA/task1/util/__init__.py
Normal file
0
MusinAA/task1/util/__init__.py
Normal file
55
MusinAA/task1/util/randomNames.py
Normal file
55
MusinAA/task1/util/randomNames.py
Normal 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
|
||||||
37
MusinAA/task1/util/timeTester.py
Normal file
37
MusinAA/task1/util/timeTester.py
Normal 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
|
||||||
|
}
|
||||||
0
MusinAA/task2/__init__.py
Normal file
0
MusinAA/task2/__init__.py
Normal file
72
MusinAA/task2/mazeBuilder.py
Normal file
72
MusinAA/task2/mazeBuilder.py
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from itertools import product
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from task2.mazeObjects.maze import Maze
|
||||||
|
from task2.mazeObjects.cell import Cell
|
||||||
|
|
||||||
|
class MazeBuilder(ABC):
|
||||||
|
"""Интерфейс MazeBuilder с методом buildFromFile(filename)"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def buildFromFile(self, filename: str):
|
||||||
|
"""Создание лабиринта из файла."""
|
||||||
|
|
||||||
|
|
||||||
|
class TextFileMazeBuilder(MazeBuilder):
|
||||||
|
"""Читает файл, парсит символы,
|
||||||
|
создаёт объекты Cell,
|
||||||
|
задаёт координаты и флаги,
|
||||||
|
после чего возвращает готовый Maze."""
|
||||||
|
|
||||||
|
start = {'x': 0, 'y': 0}
|
||||||
|
end = {'x': 0, 'y': 0}
|
||||||
|
|
||||||
|
def cellStrategy(self, letter: str) -> Cell:
|
||||||
|
if letter == '#':
|
||||||
|
return Cell(isWall=True)
|
||||||
|
elif letter == ' ':
|
||||||
|
return Cell()
|
||||||
|
elif letter == 'S':
|
||||||
|
return Cell(isStart=True)
|
||||||
|
elif letter == 'E':
|
||||||
|
return Cell(isExit=True)
|
||||||
|
else:
|
||||||
|
sys.stderr.write(f"Неизвестный символ '{letter}' при загрузке из файла\n")
|
||||||
|
return Cell()
|
||||||
|
|
||||||
|
def updateStartEnd(self, letter: str, x:int, y:int) -> None:
|
||||||
|
if letter == 'S':
|
||||||
|
self.start = {'x': x, 'y': y}
|
||||||
|
elif letter == 'E':
|
||||||
|
self.end = {'x': x, 'y': y}
|
||||||
|
|
||||||
|
def generate_row_from_txt(self, filename: str) -> list[str]:
|
||||||
|
with open(filename) as file:
|
||||||
|
text = file.read()
|
||||||
|
text = text.strip()
|
||||||
|
if not text:
|
||||||
|
raise ValueError(f"Файл \"{filename}\" пуст")
|
||||||
|
text = text.split('\n')
|
||||||
|
return text
|
||||||
|
|
||||||
|
def buildFromFile(self, filename: str):
|
||||||
|
rows = self.generate_row_from_txt(filename)
|
||||||
|
height = len(rows)
|
||||||
|
width = len(rows[0])
|
||||||
|
array = [[Cell() for j in range(width)] for i in range(height)]
|
||||||
|
|
||||||
|
# Здесь x и y где-то перепутаны, но мне лень это чинить
|
||||||
|
try:
|
||||||
|
for x, y in product(range(width), range(height)):
|
||||||
|
cell = self.cellStrategy(rows[y][x])
|
||||||
|
self.updateStartEnd(rows[y][x], x, y)
|
||||||
|
cell.x = x
|
||||||
|
cell.y = y
|
||||||
|
array[y][x] = cell
|
||||||
|
except IndexError:
|
||||||
|
raise ValueError(f"Строка {x+1} имеет длину {len(rows[x])}, ожидалось {width}")
|
||||||
|
|
||||||
|
return Maze(array, self.start, self.end)
|
||||||
|
|
||||||
|
|
||||||
0
MusinAA/task2/mazeObjects/__init__.py
Normal file
0
MusinAA/task2/mazeObjects/__init__.py
Normal file
13
MusinAA/task2/mazeObjects/cell.py
Normal file
13
MusinAA/task2/mazeObjects/cell.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
class Cell:
|
||||||
|
"""Хранит координаты (x, y)
|
||||||
|
флаги isWall, isStart, isExit
|
||||||
|
метод isPassable() (возвращает True для прохода, если не стена)."""
|
||||||
|
def __init__(self, x: int = 0, y: int = 0, isWall:bool = False, isStart:bool = False, isExit:bool = False):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.isWall = isWall
|
||||||
|
self.isStart = isStart
|
||||||
|
self.isExit = isExit
|
||||||
|
|
||||||
|
def isPassable(self):
|
||||||
|
return not self.isWall
|
||||||
40
MusinAA/task2/mazeObjects/maze.py
Normal file
40
MusinAA/task2/mazeObjects/maze.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
from task2.mazeObjects.cell import Cell
|
||||||
|
|
||||||
|
class Maze:
|
||||||
|
"""Хранит двумерный массив клеток,
|
||||||
|
ширину, высоту, ссылки на стартовую и выходную клетку.
|
||||||
|
Методы:
|
||||||
|
getCell(x, y), getNeighbors(cell) – возвращает список соседних проходимых клеток
|
||||||
|
(вверх, вниз, влево, вправо, если в пределах границ и не стена)."""
|
||||||
|
|
||||||
|
def __init__(self, mazeArray: list[list[Cell]], start: dict, end: dict) -> None:
|
||||||
|
self.mazeArray = mazeArray
|
||||||
|
self.width = len(mazeArray)
|
||||||
|
self.height = len(mazeArray[0])
|
||||||
|
|
||||||
|
self.startCell = self.getCell(start['x'], start['y'])
|
||||||
|
self.endCell = self.getCell(end['x'], end['y'])
|
||||||
|
|
||||||
|
def getCell(self, x: int, y: int):
|
||||||
|
return self.mazeArray[y][x]
|
||||||
|
|
||||||
|
def checkCell(self, x: int, y: int):
|
||||||
|
if not(0 <= x and x < self.width):
|
||||||
|
return False
|
||||||
|
if not(0 <= y and y < self.height):
|
||||||
|
return False
|
||||||
|
return self.getCell(x, y).isPassable()
|
||||||
|
|
||||||
|
def getNeighbors(self, cell: Cell):
|
||||||
|
point = (cell.x, cell.y)
|
||||||
|
offsets = ((0, 1),
|
||||||
|
(0, -1),
|
||||||
|
(-1, 0),
|
||||||
|
(1, 0))
|
||||||
|
passableCells = []
|
||||||
|
for ofst in offsets:
|
||||||
|
x = point[0]+ofst[0]
|
||||||
|
y = point[1]+ofst[1]
|
||||||
|
if self.checkCell(x, y):
|
||||||
|
passableCells.append(self.getCell(x, y))
|
||||||
|
return passableCells
|
||||||
Loading…
Reference in New Issue
Block a user