forked from UNN/2026-rff_mp
212 lines
8.1 KiB
Python
212 lines
8.1 KiB
Python
|
|
import random
|
||
|
|
import time
|
||
|
|
import csv
|
||
|
|
|
||
|
|
# ---------- Реализация связного списка ----------
|
||
|
|
def ll_insert_begin(head, name, phone):
|
||
|
|
# Вставка узла в начало списка. Возвращает новую голову.
|
||
|
|
new_node = {'name': name, 'phone': phone, 'next': head}
|
||
|
|
return new_node
|
||
|
|
|
||
|
|
def ll_find(head, name):
|
||
|
|
# Поиск телефона по имени. Возвращает phone или None.
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
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:
|
||
|
|
return head['next']
|
||
|
|
current = head
|
||
|
|
while current['next']:
|
||
|
|
if current['next']['name'] == name:
|
||
|
|
current['next'] = current['next']['next']
|
||
|
|
return head
|
||
|
|
current = current['next']
|
||
|
|
return head
|
||
|
|
|
||
|
|
def ll_list_all(head):
|
||
|
|
# Собирает все записи в список и сортирует по имени.
|
||
|
|
records = []
|
||
|
|
current = head
|
||
|
|
while current:
|
||
|
|
records.append((current['name'], current['phone']))
|
||
|
|
current = current['next']
|
||
|
|
records.sort(key=lambda x: x[0])
|
||
|
|
return records
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Измерения для массива ----------
|
||
|
|
def array_insert_measure(records, sorted_flag=False):
|
||
|
|
# Вставка записей в начало массива. Возвращает время.
|
||
|
|
arr = []
|
||
|
|
start = time.perf_counter()
|
||
|
|
if sorted_flag:
|
||
|
|
# records уже отсортированы
|
||
|
|
for item in records:
|
||
|
|
arr.insert(0, item)
|
||
|
|
else:
|
||
|
|
for item in records:
|
||
|
|
arr.insert(0, item)
|
||
|
|
end = time.perf_counter()
|
||
|
|
return end - start
|
||
|
|
|
||
|
|
def array_find_measure(records, test_names):
|
||
|
|
# Поиск в массиве: линейный перебор.
|
||
|
|
start = time.perf_counter()
|
||
|
|
for name in test_names:
|
||
|
|
for rec in records:
|
||
|
|
if rec[0] == name:
|
||
|
|
break
|
||
|
|
end = time.perf_counter()
|
||
|
|
return end - start
|
||
|
|
|
||
|
|
def array_delete_measure(records, delete_names):
|
||
|
|
# Удаление из массива через создание нового списка (как в оригинале).
|
||
|
|
times = []
|
||
|
|
for name in delete_names:
|
||
|
|
start = time.perf_counter()
|
||
|
|
records = [rec for rec in records if rec[0] != name]
|
||
|
|
end = time.perf_counter()
|
||
|
|
times.append(end - start)
|
||
|
|
return sum(times) / len(times) if times else 0
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Измерения для связного списка ----------
|
||
|
|
def linked_insert_measure(records, sorted_flag=False):
|
||
|
|
# Вставка записей в начало связного списка. Возвращает время.
|
||
|
|
head = None
|
||
|
|
start = time.perf_counter()
|
||
|
|
# Если sorted_flag == True, records уже отсортированы, но для связного списка
|
||
|
|
# вставка в начало всегда O(1), порядок не влияет на время.
|
||
|
|
for name, phone in records:
|
||
|
|
head = ll_insert_begin(head, name, phone)
|
||
|
|
end = time.perf_counter()
|
||
|
|
return end - start
|
||
|
|
|
||
|
|
def linked_find_measure(head, test_names):
|
||
|
|
# Поиск в связном списке.
|
||
|
|
start = time.perf_counter()
|
||
|
|
for name in test_names:
|
||
|
|
ll_find(head, name)
|
||
|
|
end = time.perf_counter()
|
||
|
|
return end - start
|
||
|
|
|
||
|
|
def linked_delete_measure(head, delete_names):
|
||
|
|
# Удаление из связного списка.
|
||
|
|
times = []
|
||
|
|
for name in delete_names:
|
||
|
|
start = time.perf_counter()
|
||
|
|
head = ll_delete(head, name)
|
||
|
|
end = time.perf_counter()
|
||
|
|
times.append(end - start)
|
||
|
|
return sum(times) / len(times) if times else 0
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
# ---------- Основная функция эксперимента ----------
|
||
|
|
def main():
|
||
|
|
N = 10000
|
||
|
|
# Генерация тестовых данных
|
||
|
|
records = []
|
||
|
|
for i in range(N):
|
||
|
|
name = f"User_{i:05d}"
|
||
|
|
phone = f"8{random.randint(9000000000, 9999999999)}"
|
||
|
|
records.append((name, phone))
|
||
|
|
|
||
|
|
records_shuffled = records.copy()
|
||
|
|
random.shuffle(records_shuffled)
|
||
|
|
records_sorted = sorted(records, key=lambda x: x[0])
|
||
|
|
|
||
|
|
# Имена для поиска (100 существующих + 10 несуществующих)
|
||
|
|
existing_names = random.sample([rec[0] for rec in records], 100)
|
||
|
|
non_existing = [f"None_{i}" for i in range(10)]
|
||
|
|
test_names = existing_names + non_existing
|
||
|
|
|
||
|
|
# Имена для удаления (50 случайных)
|
||
|
|
delete_names = random.sample([rec[0] for rec in records], 50)
|
||
|
|
|
||
|
|
# Результаты будем собирать в список списков
|
||
|
|
results = [["Структура", "Режим", "Операция", "Время (сек)"]]
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
# ----- Массив -----
|
||
|
|
# Вставка (случайный порядок)
|
||
|
|
arr_time_shuffled = 0.0
|
||
|
|
arr_time_sorted = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
arr_time_shuffled += array_insert_measure(records_shuffled, sorted_flag=False)
|
||
|
|
arr_time_sorted += array_insert_measure(records_sorted, sorted_flag=True)
|
||
|
|
results.append(["Array", "случайный", "вставка (в начало)", arr_time_shuffled / 5])
|
||
|
|
results.append(["Array", "отсортированный", "вставка (в начало)", arr_time_sorted / 5])
|
||
|
|
|
||
|
|
# Поиск
|
||
|
|
find_time = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
find_time += array_find_measure(records, test_names)
|
||
|
|
results.append(["Array", "любой", "поиск 110 записей", find_time / 5])
|
||
|
|
|
||
|
|
# Удаление
|
||
|
|
del_time = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
del_time += array_delete_measure(records.copy(), delete_names)
|
||
|
|
results.append(["Array", "любой", "удаление 50 записей (среднее)", del_time / 5])
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
# ----- Связный список -----
|
||
|
|
# Вставка
|
||
|
|
ll_time_shuffled = 0.0
|
||
|
|
ll_time_sorted = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
ll_time_shuffled += linked_insert_measure(records_shuffled)
|
||
|
|
ll_time_sorted += linked_insert_measure(records_sorted)
|
||
|
|
results.append(["Linked list", "случайный", "вставка (в начало)", ll_time_shuffled / 5])
|
||
|
|
results.append(["Linked list", "отсортированный", "вставка (в начало)", ll_time_sorted / 5])
|
||
|
|
|
||
|
|
# Поиск (предварительно строим список)
|
||
|
|
head = None
|
||
|
|
for name, phone in records:
|
||
|
|
head = ll_insert_begin(head, name, phone)
|
||
|
|
find_time_ll = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
find_time_ll += linked_find_measure(head, test_names)
|
||
|
|
results.append(["Linked list", "любой", "поиск 110 записей", find_time_ll / 5])
|
||
|
|
|
||
|
|
# Удаление (копируем список для каждого замера)
|
||
|
|
del_time_ll = 0.0
|
||
|
|
for _ in range(5):
|
||
|
|
# Строим новую копию списка
|
||
|
|
h = None
|
||
|
|
for name, phone in records:
|
||
|
|
h = ll_insert_begin(h, name, phone)
|
||
|
|
del_time_ll += linked_delete_measure(h, delete_names)
|
||
|
|
results.append(["Linked list", "любой", "удаление 50 записей (среднее)", del_time_ll / 5])
|
||
|
|
|
||
|
|
# ----- Вывод результатов в единый столбец -----
|
||
|
|
print("\nРезультаты экспериментов (время в секундах):\n")
|
||
|
|
# Определяем максимальную ширину первого столбца для красивого выравнивания
|
||
|
|
col_widths = [max(len(str(row[i])) for row in results) for i in range(4)]
|
||
|
|
for row in results:
|
||
|
|
print(f"{row[0]:<{col_widths[0]}} {row[1]:<{col_widths[1]}} "
|
||
|
|
f"{row[2]:<{col_widths[2]}} {row[3]:<{col_widths[3]}}")
|
||
|
|
|
||
|
|
# ----- Запись результатов в CSV-файл -----
|
||
|
|
with open('results.csv', 'w', newline='', encoding='utf-8-sig') as csvfile:
|
||
|
|
writer = csv.writer(csvfile, delimiter = ';')
|
||
|
|
writer.writerows(results)
|
||
|
|
|
||
|
|
print("\nРезультаты сохранены в файл 'results.csv'.")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|