[1] task-1-data-structures #163

Open
ProninVV wants to merge 12 commits from ProninVV/2026-rff_mp:task-1-data-structures into develop
17 changed files with 34674 additions and 0 deletions

16
.gitignore vendored
View File

@ -160,3 +160,19 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Игнорируем CSV
aufgabe-1-data-structures/results/timedata_*.csv
# разрешаем средние данные
!aufgabe-1-data-structures/results/aaverage_*.csv
# Мусор LaTeX
*.aux
*.synctex.gz
*.toc
*.log

View File

@ -0,0 +1,250 @@
# LInkedList (Node = List = {'name': 'Имя', 'phone': '123', 'next': None}) имена уникальные (id)
def ll_insert(head, name, phone):
""" проходит до конца (или сразу добавляет в конец) и возвращает новую голову
(если вставка в начало) или изменяет список по ссылке. Удобнее возвращать новую
голову, если вставка может быть в начало """
new_node = {'name': name, 'phone': phone, 'next': None}
# если списка не было
if head is None:
return new_node
# # вставка в начало O(1)
# new_node = {'name': name, 'phone': phone, 'next': head}
# return new_node
# вставка в конец O(n)
current = head
while current['next'] is not None:
# проверка существования данного идентификатора (обновляем запись)
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
# проверка на id
if current['name'] == name:
current['phone'] = phone
else: current['next'] = new_node
return head
def ll_find(head, name):
""" ищет узел, возвращает телефон или None """
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
""" удаляет узел, возвращает новую голову """
if head is None:
return None
# Удаление первого
if head['name'] == name:
new_head = head['next']
head['next'] = None
return new_head
# Если не первый
current = head
while current['next'] is not None:
if current['next']['name'] == name:
target = current['next']
current['next'] = target['next']
target['next'] = None
return head
current = current['next']
return head
def ll_list_all(head):
""" собирает все записи в список и сортирует (сортировка вынесена отдельно) """
length = ll_Lenght(head)
new_list = [None]*length
current = head
for i in range(length):
new_list[i] = (current['name'], current['phone'])
current = current['next']
sorten(new_list)
return new_list
# вспомогательные функции--------------------------------
def ll_Lenght(head):
# длина связного списка
counter = 0
curr = head
while curr:
counter += 1
curr = curr['next']
return counter
def sorten(arr):
n = len(arr)
for i in range(n):
for j in range(0, n - i - 1):
if arr[j][0] > arr[j + 1][0]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
# -----------------------------------------------------------
# HashTable (Хранится как список buckets фиксированной длины, каждый элемент — голова связного списка (или None))
def hash_table(size):
return [None]*size
def hash_func(name, buckets_count):
h = 0
for char in name:
h += ord(char)
return h % buckets_count
def ht_insert(buckets, name, phone):
""" вычисляет индекс, вызывает ll_insert для соответствующего бакета """
if buckets is None:
return
index = hash_func(name, len(buckets))
buckets[index] = ll_insert(buckets[index], name, phone)
def ht_find(buckets, name):
""" """
idx = hash_func(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
idx = hash_func(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
""" собирает все записи из всех бакетов и сортирует """
total_count = 0
for head in buckets:
total_count += ll_Lenght(head)
full_data = [None]*total_count
k = 0
for head in buckets:
curr = head
while curr:
full_data[k] = (curr['name'], curr['phone'])
k += 1
curr = curr['next']
sorten(full_data)
return full_data
# Двоичное дерево поиска : Узел — словарь: {'name': 'Имя', 'phone': '123', 'left': None, 'right': None}
def bst_insert(root, name, phone):
""" рекурсивно или итеративно вставляет, возвращает новый корень (если корень меняется) """
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
# если дерева нет
if root is None:
return new_node
if name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
elif name > root['name']:
root['right'] = bst_insert(root['right'], name, phone)
else:
root['phone'] = phone
return root
def bst_find(root, name):
""" поиск """
if root is None:
return None
if root['name'] == name:
return root['phone']
elif name < root['name']:
return bst_find(root['left'], name)
elif name > root['name']:
return bst_find(root['right'], name)
def bst_delete(root, name):
""" удаление, возвращает новый корень """
if root is None:
return None
# спускаемся к нужному узлу (аналогично поиску)
elif name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
# стоим в нужном узле
else:
# узла слева нет (вернет правого ребенка или None)
if root['left'] is None:
return root['right']
# узла справа нет (вернет левого ребенка)
if root['right'] is None:
return root['left']
# два наследника (поиск минимального поддерева в правой ветке)
successor = root['right']
while successor['left'] is not None:
successor = successor['left']
root['name'] = successor['name']
root['phone'] = successor['phone']
# Удаляем дубликат преемника в правом поддереве
root['right'] = bst_delete(root['right'], successor['name'])
return root
def bst_list_all(root, result=None):
""" центрированный обход (рекурсивно собирает записи в отсортированном порядке) """
if result is None:
result = []
# сначала спускаемся по левой стороне вниз, затем идем вверх и вправо
if root is not None:
bst_list_all(root['left'], result)
result.append((root['name'], root['phone']))
bst_list_all(root['right'], result)
return result

View File

@ -0,0 +1,20 @@
import pandas as pd
import glob
folder_path = 'results'
sizes = ['500', '1000', '2000', '5000', '10000']
for size in sizes:
files = glob.glob(f'{folder_path}/timedata_{size}_epochs_*.csv')
data = [pd.read_csv(f)['Время (сек)'] for f in files]
datatomean = pd.concat(data, axis=1)
datamean = datatomean.mean(axis=1)
df = pd.read_csv(files[0])
df['Время (сек)'] = datamean
df.to_csv(f'results/aaverage_timedata_{size}.csv', index=False)

View File

@ -0,0 +1,94 @@
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import AutoMinorLocator
df500 = pd.read_csv("results/aaverage_timedata_500.csv")
df1000 = pd.read_csv("results/aaverage_timedata_1000.csv")
df2000 = pd.read_csv("results/aaverage_timedata_2000.csv")
df5000 = pd.read_csv("results/aaverage_timedata_5000.csv")
df10000 = pd.read_csv("results/aaverage_timedata_10000.csv")
def select_data_list(ax):
dfs = [df500, df1000, df2000, df5000, df10000]
Nvals = [500, 1000, 2000, 5000, 10000]
# delete, find, insert
# список:
valsSort = [list(arr[(arr['Структура'] == "linklist") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
valsShuff = [list(arr[(arr['Структура'] == "linklist") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
# 0 - sorted 1 - shuffled
# delete
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
# find
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
# insert
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
def select_data_hasht(ax):
dfs = [df500, df1000, df2000, df5000, df10000]
Nvals = [500, 1000, 2000, 5000, 10000]
# delete, find, insert
# список:
valsSort = [list(arr[(arr['Структура'] == "hashtable") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
valsShuff = [list(arr[(arr['Структура'] == "hashtable") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
# 0 - sorted 1 - shuffled
# delete
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
# find
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
# insert
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
def select_data_tree(ax):
dfs = [df500, df1000, df2000, df5000, df10000]
Nvals = [500, 1000, 2000, 5000, 10000]
# delete, find, insert
# список:
valsSort = [list(arr[(arr['Структура'] == "bintree") & (arr['Режим'] == "sorted")]["Время (сек)"]) for arr in dfs]
valsShuff = [list(arr[(arr['Структура'] == "bintree") & (arr['Режим'] == "shuffled")]["Время (сек)"]) for arr in dfs]
# 0 - sorted 1 - shuffled
# delete
ax[0].plot(Nvals, [row[0] for row in valsSort], label="delete", color='red')
ax[1].plot(Nvals, [row[0] for row in valsShuff], color='red')
# find
ax[0].plot(Nvals, [row[1] for row in valsSort], label="find", color='blue')
ax[1].plot(Nvals, [row[1] for row in valsShuff], color='blue')
# insert
ax[0].plot(Nvals, [row[2] for row in valsSort], label="insert", color='green')
ax[1].plot(Nvals, [row[2] for row in valsShuff], color='green')
# построение графика
def design_show_graph(title, version, ymaxlim):
fig, ax = plt.subplots(figsize=(10, 5), nrows=1, ncols=2)
for i in range(2):
match title:
case "Tree":
select_data_tree(ax)
case "Linklist":
select_data_list(ax)
case "hasht":
select_data_hasht(ax)
ax[0].set_title(f"График сложностей для {title} (sort)")
ax[1].set_title(f"График сложностей для {title} (shuff)")
ax[i].set_xlabel("N")
ax[i].set_ylabel("сек * ")
ax[i].grid(which="major", linewidth=1.5)
ax[i].grid(which="minor", color="gray", linewidth=0.5)
ax[i].xaxis.set_minor_locator(AutoMinorLocator())
ax[i].yaxis.set_minor_locator(AutoMinorLocator())
ax[i].legend()
ax[i].set_ylim(0, ymaxlim)
plt.savefig(f'graphics\{title}{version}.png', dpi=200)
plt.savefig(f'graphics\T{title}{version}.eps', dpi=200)
plt.show()
design_show_graph("hasht", 2, 0.4)

Binary file not shown.

View File

@ -0,0 +1,136 @@
\input{preambule.tex}
\begin{document}
% --- ТИТУЛЬНЫЙ ЛИСТ (упрощенно) ---
\begin{titlepage}
\centering
МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ РФ \\
«Национальный исследовательский Нижегородский государственный университет им. Н.И. Лобачевского» \\
\vspace{4cm}
\Large ОТЧЕТ К ЛАБОРАТОРНОЙ РАБОТЕ \\
\vspace{1cm}
\large «Реализация и экспериментальное сравнение базовых структур данных на примере телефонного справочника» \\
\vspace{4cm}
\flushright
Выполнил: студент В. В. Пронин \\
Преподаватель: Н. С. Морозов \\
\vfill
Нижний Новгород \\
2024
\end{titlepage}
\newpage
\tableofcontents
\newpage
\section{Введение}
Эффективность программных систем во многом определяется выбором способов организации данных в оперативной памяти. Задача разработки телефонного справочника является классическим примером, требующим баланса между скоростью вставки новых записей, поиском по ключу и эффективным удалением.
В рамках данной работы исследуются три фундаментальные структуры данных, реализованные «с нуля» в процедурной парадигме программирования на языке Python:
\begin{itemize}
\item \textbf{Связный список (Linked List)} --- динамическая структура, позволяющая оценить базовые механизмы управления указателями и демонстрирующая линейную сложность операций $O(n)$.
\item \textbf{Хеш-таблица (Hash Table)} --- ассоциативный массив, использующий хеширование для обеспечения прямого доступа к данным. Реализация позволяет изучить методы разрешения коллизий и преимущества константной сложности $O(1)$.
\item \textbf{Двоичное дерево поиска (BST)} --- иерархическая структура, обеспечивающая логарифмическую скорость доступа $O(\log n)$ и поддерживающая упорядоченность данных «из коробки».
\end{itemize}
\textbf{Цель работы:} Изучить внутренние алгоритмы работы перечисленных структур, реализовать их без использования встроенных высокоуровневых контейнеров и экспериментально подтвердить теоретические оценки временной сложности на случайных и отсортированных наборах данных.
\section{Реализация структур данных}
\subsection{Связный список}
% Здесь опишите логику ll_insert, ll_find и ll_delete
\subsection{Хеш-таблица}
% Опишите хеш-функцию и метод цепочек
\subsection{Двоичное дерево поиска}
% Опишите рекурсивные алгоритмы и проблему деградации
\section{Методика эксперимента}
Замеры производились для наборов данных объемом $N=500, 1000, 2000, 5000, 10000$ элементов. Использовались два сценария: перемешанные (\textit{shuffled}) и отсортированные по алфавиту (\textit{sorted}) записи. Каждая операция выполнялась 5 раз с последующим вычислением среднего арифметического значения с помощью функции \texttt{time.perf\_counter()}.
\section{Результаты и анализ}
Было проведено серию опытов для $N$ от 500 до 10000.
\subsection*{1. Бинарное дерево поиска (BST) и влияние порядка}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/TTree1.eps}
\caption{Зависимость времени выполнения операций в BST от объема данных}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/TTree2.eps}
\end{figure}
\begin{itemize}
\item \textbf{Деградация на отсортированных данных:} При вставке отсортированных данных время увеличилось с \textbf{0.124с} ($N=1000$) до \textbf{13.27с} ($N=10000$). Рост времени в 100 раз при увеличении объема данных в 10 раз четко указывает на квадратичную сложность $O(n^2)$ для процесса заполнения всей структуры. Дерево выродилось в линейный список, и поиск места вставки стал занимать $O(n)$ вместо ожидаемого $O(\log n)$.
\item \textbf{Эффективность на перемешанных данных:} На \texttt{shuffled} данных вставка 10000 элементов заняла всего \textbf{0.031с}. Это подтверждает логарифмическую сложность $O(\log n)$ для операций в дереве при случайном распределении ключей.
\end{itemize}
\subsection*{2. Хеш-таблица: Стабильность и скорость}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/Thasht1.eps}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/Thasht2.eps}
\end{figure}
\begin{itemize}
\item \textbf{Чувствительность к порядку:} Хеш-таблица показала идентичные результаты как на \texttt{shuffled}, так и на \texttt{sorted} данных (около \textbf{0.165с} -- \textbf{0.167с} для 10000 вставок). Это объясняется тем, что хеш-функция распределяет ключи по бакетам независимо от их исходного порядка, предотвращая деградацию структуры.
\item \textbf{Превосходство:} На больших объемах хеш-таблица оказалась самой быстрой структурой для поиска и удаления ($\approx 0.001$с при $N=10000$), что подтверждает теоретическую среднюю сложность $O(1)$.
\item \textbf{Замечание:} Так как реализация использует списки для разрешения коллизий со вставкой в конец, при заполнении таблицы наблюдается рост времени вставки, стремящийся к квадратичному, однако абсолютные значения остаются на порядки ниже, чем у выродившегося BST.
\end{itemize}
\subsection*{3. Связный список: Линейная зависимость}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/Tlinklist1.eps}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[scale=0.7]{plots/Tlinklist2.eps}
\end{figure}
\begin{itemize}
\item \textbf{Поиск и удаление:} Связный список показал худшие результаты среди всех структур на случайных данных. Время поиска при 10000 элементах (\textbf{0.029с}) значительно медленнее, чем у BST на перемешанных данных (\textbf{0.0002с}). Это подтверждает линейную сложность $O(n)$.
\item \textbf{Вставка:} Вставка (вероятно, в конец или с сохранением порядка) дает $O(n^2)$ при заполнении (\textbf{2.83с} -- \textbf{3.00с} на 10000 эл.). Характер роста времени при переходе от $N=5000$ (\textbf{0.71с}) к $N=10000$ подтверждает квадратичную зависимость.
\end{itemize}
\subsection*{Вывод: выбор структуры данных}
\begin{enumerate}
\item \textbf{Хеш-таблица} — наиболее универсальный выбор. Она обеспечивает стабильное $O(1)$ для поиска и не зависит от порядка входящих данных.
\item \textbf{BST} — крайне эффективен ($O(\log n)$) при случайном распределении данных, но без механизмов самобалансировки критически уязвим к отсортированным входным последовательностям, замедляясь до уровня списка.
\item \textbf{Связный список} — продемонстрировал самую низкую производительность на операциях поиска и массовой вставки. Его использование оправдано только в специфических сценариях (например, реализация стека), где работа ведется исключительно с головой списка за $O(1)$.
\end{enumerate}
\subsection*{Сводная таблица результатов}
\begin{table}[H]
\centering
\small
\begin{tabular}{|l|l|c|c|c|c|c|}
\hline
\textbf{Структура} & \textbf{Режим} & \textbf{Опер.} & \textbf{N=500} & \textbf{N=1000} & \textbf{N=5000} & \textbf{N=10000} \\ \hline
\multirow{3}{*}{LinkList} & Shuffled & Insert & 0.0066 & 0.0292 & 0.7089 & 2.8358 \\
& Shuffled & Find & 0.0012 & 0.0026 & 0.0147 & 0.0289 \\
& Sorted & Insert & 0.0065 & 0.0290 & 0.7637 & 3.0042 \\ \hline
\multirow{3}{*}{HashTable} & Shuffled & Insert & 0.0007 & 0.0022 & 0.0468 & 0.1670 \\
& Shuffled & Find & 0.0001 & 0.0002 & 0.0008 & 0.0014 \\
& Sorted & Insert & 0.0007 & 0.0022 & 0.0448 & 0.1646 \\ \hline
\multirow{3}{*}{BinTree} & Shuffled & Insert & 0.0009 & 0.0021 & 0.0145 & 0.0309 \\
& Shuffled & Find & 0.0001 & 0.0001 & 0.0002 & 0.0002 \\
& Sorted & Insert & \textbf{0.0298} & \textbf{0.1239} & \textbf{3.3052} & \textbf{13.2706} \\ \hline
\end{tabular}
\caption{Сравнение минимального времени выполнения операций (в секундах) в зависимости от объема данных $N$}
\end{table}
\end{document}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
%\documentclass[a4paper, 12pt]{article}
\documentclass[a4paper, 14pt]{extarticle}
\usepackage[english, russian]{babel}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{comment}
\usepackage{multirow}
\usepackage{fontspec}
\setmainfont{Times New Roman}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{geometry}
\usepackage{titleps}
\usepackage{graphicx}
\DeclareGraphicsExtensions{.pdf, .jpg}
\usepackage{wrapfig}
\usepackage{indentfirst}
\geometry{top=20mm}
\geometry{bottom=25mm}
\geometry{left=30mm}
\geometry{right=10mm}
\usepackage{float}
\usepackage{wrapfig}
\newpagestyle{main}{
\setheadrule{0.4pt}
\sethead{ННГУ им Н.И. Лобачесвкого}{}{В. В. Пронин}
\setfoot{}{\thepage}{}
}
\pagestyle{main}
%\setcounter{page}{2}
\linespread{1.5}
\setlength{\parindent}{10mm}
\setlength{\parskip}{1ex}

View File

@ -0,0 +1,19 @@
Структура,Режим,Операция,Время (сек)
linklist,shuffled,insert,0.02917951999970676
linklist,shuffled,find,0.00256621999997146
linklist,shuffled,delete,0.0018302000000403
hashtable,shuffled,insert,0.00223439999972464
hashtable,shuffled,find,0.00018408000032643998
hashtable,shuffled,delete,0.00013254000023147998
bintree,shuffled,insert,0.00211651999998134
bintree,shuffled,find,0.00014015999986434
bintree,shuffled,delete,7.299999997485429e-05
linklist,sorted,insert,0.02902601999994654
linklist,sorted,find,0.00272362000014248
linklist,sorted,delete,0.0017690399998172598
hashtable,sorted,insert,0.00219620000007122
hashtable,sorted,find,0.00018717999992074
hashtable,sorted,delete,0.00011756000003512
bintree,sorted,insert,0.12391134000008604
bintree,sorted,find,0.0079993400002422
bintree,sorted,delete,0.004170019999764881
1 Структура Режим Операция Время (сек)
2 linklist shuffled insert 0.02917951999970676
3 linklist shuffled find 0.00256621999997146
4 linklist shuffled delete 0.0018302000000403
5 hashtable shuffled insert 0.00223439999972464
6 hashtable shuffled find 0.00018408000032643998
7 hashtable shuffled delete 0.00013254000023147998
8 bintree shuffled insert 0.00211651999998134
9 bintree shuffled find 0.00014015999986434
10 bintree shuffled delete 7.299999997485429e-05
11 linklist sorted insert 0.02902601999994654
12 linklist sorted find 0.00272362000014248
13 linklist sorted delete 0.0017690399998172598
14 hashtable sorted insert 0.00219620000007122
15 hashtable sorted find 0.00018717999992074
16 hashtable sorted delete 0.00011756000003512
17 bintree sorted insert 0.12391134000008604
18 bintree sorted find 0.0079993400002422
19 bintree sorted delete 0.004170019999764881

View File

@ -0,0 +1,19 @@
Структура,Режим,Операция,Время (сек)
linklist,shuffled,insert,2.835846880000099
linklist,shuffled,find,0.02894071999999136
linklist,shuffled,delete,0.017179720000240158
hashtable,shuffled,insert,0.16700972000016914
hashtable,shuffled,find,0.0014067599999179402
hashtable,shuffled,delete,0.00103095999966166
bintree,shuffled,insert,0.030944720000115878
bintree,shuffled,find,0.00019450000017964003
bintree,shuffled,delete,9.787999988471869e-05
linklist,sorted,insert,3.0041990600000643
linklist,sorted,find,0.02895102000002222
linklist,sorted,delete,0.016321099999913664
hashtable,sorted,insert,0.16461017999990868
hashtable,sorted,find,0.0014511600000332201
hashtable,sorted,delete,0.0010335000002669001
bintree,sorted,insert,13.270635900000162
bintree,sorted,find,0.08588061999998894
bintree,sorted,delete,0.04398507999994758
1 Структура Режим Операция Время (сек)
2 linklist shuffled insert 2.835846880000099
3 linklist shuffled find 0.02894071999999136
4 linklist shuffled delete 0.017179720000240158
5 hashtable shuffled insert 0.16700972000016914
6 hashtable shuffled find 0.0014067599999179402
7 hashtable shuffled delete 0.00103095999966166
8 bintree shuffled insert 0.030944720000115878
9 bintree shuffled find 0.00019450000017964003
10 bintree shuffled delete 9.787999988471869e-05
11 linklist sorted insert 3.0041990600000643
12 linklist sorted find 0.02895102000002222
13 linklist sorted delete 0.016321099999913664
14 hashtable sorted insert 0.16461017999990868
15 hashtable sorted find 0.0014511600000332201
16 hashtable sorted delete 0.0010335000002669001
17 bintree sorted insert 13.270635900000162
18 bintree sorted find 0.08588061999998894
19 bintree sorted delete 0.04398507999994758

View File

@ -0,0 +1,140 @@
from aufg1 import *
import time
import random
import sys
import csv
sys.setrecursionlimit(20000)
def phone_number_generate():
number = "8"
text = "0123456789"
for i in range(10):
char = random.choice(text)
number += char
return number
def create_data(n=100):
""" создаем сразу обычный массив и остортированный """
records_sorted = []
for i in range(n):
name = f"User_{i:05d}"
phone = phone_number_generate()
records_sorted.append((name, phone))
records_shuffled = records_sorted[:]
random.shuffle(records_shuffled)
return records_sorted, records_shuffled
def run_expirement(epoch=1, elements=1000):
""" распределяем данные по трем структурам данных
тестируем время операций (вставки, удаления, перебора) и записываем полученные результаты в файл """
header = ["Структура", "Режим", "Операция", "Время (сек)"]
for j in range(epoch):
print(f"эпоха - {j+1}")
results = [header]
# создаем данные
records_sorted, records_shuffled = create_data(elements)
datasets = [
("shuffled", records_shuffled),
("sorted", records_sorted)]
# сразу будем обрабатывать и случайны и отсортированный данные
for label, arr in datasets:
linklist = None
hashtab = hash_table(elements)
bintree = None
# заполнение связного списка
start = time.perf_counter()
for p in arr:
linklist = ll_insert(linklist, p[0], p[1])
end = time.perf_counter()
results.append(["linklist", label, "insert", end-start])
# поиск 110 имен в связном списке
# несуществующие данные
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
# случайная комбинация
chaossample = random.sample(arr, 100) + nonedata
start = time.perf_counter()
for p in chaossample:
ll_find(linklist, p[0])
end = time.perf_counter()
results.append(["linklist", label, "find", end-start])
# удаление 50 имен в св писке
deldata = random.sample(arr, 50)
start = time.perf_counter()
for p in deldata:
ll_delete(linklist, p[0])
end = time.perf_counter()
results.append(["linklist", label, "delete", end-start])
# заполнение хэш-тфблицы
start = time.perf_counter()
for p in arr:
ht_insert(hashtab, p[0], p[1])
end = time.perf_counter()
results.append(["hashtable", label, "insert", end-start])
# поиск 110 имен в хэш таблице
# несуществующие данные
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
# случайная комбинация
chaossample = random.sample(arr, 100) + nonedata
start = time.perf_counter()
for p in chaossample:
ht_find(hashtab, p[0])
end = time.perf_counter()
results.append(["hashtable", label, "find", end-start])
# удаление 50 имен в хэш таблице
deldata = random.sample(arr, 50)
start = time.perf_counter()
for p in deldata:
ht_delete(hashtab, p[0])
end = time.perf_counter()
results.append(["hashtable", label, "delete", end-start])
# заполнение дерева
start = time.perf_counter()
for p in arr:
bintree = bst_insert(bintree, p[0], p[1])
end = time.perf_counter()
results.append(["bintree", label, "insert", end-start])
# поиск 110 имен в дереве
# несуществующие данные
nonedata = [(f"None_{i}", phone_number_generate()) for i in range(10)]
# случайная комбинация
chaossample = random.sample(arr, 100) + nonedata
start = time.perf_counter()
for p in chaossample:
bst_find(bintree, p[0])
end = time.perf_counter()
results.append(["bintree", label, "find", end-start])
# удаление 50 имен в дереве
deldata = random.sample(arr, 50)
start = time.perf_counter()
for p in deldata:
bst_delete(bintree, p[0])
end = time.perf_counter()
results.append(["bintree", label, "delete", end-start])
filename = f"results/timedata_{elements}_epochs_{j+1}.csv"
with open(filename, mode='w', encoding='utf-8', newline='') as file:
writer = csv.writer(file)
writer.writerows(results)
run_expirement(epoch=5, elements=5000)