2026-rff_mp/main.py

454 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
import csv
import random
import copy
def ll_insert(head, name, phone):
"""Добавляет или обновляет запись. Возвращает голову."""
if head is None:
return {'name': name, 'phone': phone, 'next': None}
curr = head
while curr is not None:
if curr['name'] == name:
curr['phone'] = phone
return head
curr = curr['next']
# Вставка в конец
new_node = {'name': name, 'phone': phone, 'next': None}
curr = head
while curr['next'] is not None:
curr = curr['next']
curr['next'] = new_node
return head
def ll_find(head, name):
"""Возвращает телефон или None."""
curr = head
while curr is not None:
if curr['name'] == name:
return curr['phone']
curr = curr['next']
return None
def ll_delete(head, name):
"""Удаляет узел. Возвращает голову."""
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
curr = head['next']
while curr is not None:
if curr['name'] == name:
prev['next'] = curr['next']
return head
prev = curr
curr = curr['next']
return head
def ll_list_all(head):
"""Собирает все записи и сортирует по имени."""
records = []
curr = head
while curr is not None:
records.append((curr['name'], curr['phone']))
curr = curr['next']
records.sort(key=lambda x: x[0])
return records
def _hash(name, bucket_count):
"""Простая хеш-функция."""
return sum(ord(ch) for ch in name) % bucket_count
def ht_create(bucket_count=1000):
"""Создаёт пустую хеш-таблицу."""
return [None] * bucket_count
def ht_insert(buckets, name, phone):
"""Вставляет запись в хеш-таблицу."""
idx = _hash(name, len(buckets))
buckets[idx] = ll_insert(buckets[idx], name, phone)
def ht_find(buckets, name):
"""Ищет телефон по имени."""
idx = _hash(name, len(buckets))
return ll_find(buckets[idx], name)
def ht_delete(buckets, name):
"""Удаляет запись."""
idx = _hash(name, len(buckets))
buckets[idx] = ll_delete(buckets[idx], name)
def ht_list_all(buckets):
"""Собирает все записи из всех бакетов и сортирует."""
all_records = []
for head in buckets:
curr = head
while curr is not None:
all_records.append((curr['name'], curr['phone']))
curr = curr['next']
all_records.sort(key=lambda x: x[0])
return all_records
def bst_insert(root, name, phone):
"""Итеративная вставка. Без рекурсии — нет проблем с глубиной."""
new_node = {'name': name, 'phone': phone, 'left': None, 'right': None}
if root is None:
return new_node
current = root
while True:
if name < current['name']:
if current['left'] is None:
current['left'] = new_node
break
current = current['left']
elif name > current['name']:
if current['right'] is None:
current['right'] = new_node
break
current = current['right']
else:
current['phone'] = phone
break
return root
def bst_find(root, name):
"""Итеративный поиск."""
current = root
while current is not None:
if name == current['name']:
return current['phone']
elif name < current['name']:
current = current['left']
else:
current = current['right']
return None
def bst_min_node(node):
"""Находит минимальный узел в поддереве."""
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
"""Итеративное удаление узла."""
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
# Случай 1: нет детей
if current['left'] is None and current['right'] is None:
if parent is None:
return None
if parent['left'] is current:
parent['left'] = None
else:
parent['right'] = None
return root
# Случай 2: один ребёнок
if current['left'] is None:
child = current['right']
elif current['right'] is None:
child = current['left']
else:
# Случай 3: два ребёнка
successor_parent = current
successor = current['right']
while successor['left'] is not None:
successor_parent = successor
successor = successor['left']
current['name'] = successor['name']
current['phone'] = successor['phone']
if successor_parent['left'] is successor:
successor_parent['left'] = successor['right']
else:
successor_parent['right'] = successor['right']
return root
# Присоединяем ребёнка к родителю
if parent is None:
return child
if parent['left'] is current:
parent['left'] = child
else:
parent['right'] = child
return root
def bst_list_all(root):
"""Итеративный центрированный обход (через стек)."""
result = []
stack = []
current = root
while stack or current is not None:
while current is not None:
stack.append(current)
current = current['left']
current = stack.pop()
result.append((current['name'], current['phone']))
current = current['right']
return result
def generate_records(N=10000):
"""Возвращает (shuffled, sorted) списки записей."""
records = [(f"User_{i:05d}", f"phone_{i}") for i in range(N)]
shuffled = copy.deepcopy(records)
random.shuffle(shuffled)
return shuffled, records
def test_linked_list(records_shuffled, records_sorted, results):
N = len(records_shuffled)
# Вставка shuffled (5 повторов)
times = []
for _ in range(5):
head = None
start = time.perf_counter()
for name, phone in records_shuffled:
head = ll_insert(head, name, phone)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted
times = []
for _ in range(5):
head = None
start = time.perf_counter()
for name, phone in records_sorted:
head = ll_insert(head, name, phone)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "sorted", "insert", sum(times) / 5] + times)
# Подготовка структуры для поиска/удаления
head = None
for name, phone in records_shuffled:
head = ll_insert(head, name, phone)
# Поиск (100 существующих + 10 несуществующих)
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
ll_find(head, name)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "search", sum(times) / 5] + times)
# Удаление 50 записей
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
head_copy = None
for name, phone in records_shuffled:
head_copy = ll_insert(head_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
head_copy = ll_delete(head_copy, name)
times.append(time.perf_counter() - start)
results.append(["LinkedList", "shuffled", "delete", sum(times) / 5] + times)
def test_hash_table(records_shuffled, records_sorted, results):
N = len(records_shuffled)
bucket_count = 1000
# Вставка shuffled
times = []
for _ in range(5):
buckets = ht_create(bucket_count)
start = time.perf_counter()
for name, phone in records_shuffled:
ht_insert(buckets, name, phone)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted
times = []
for _ in range(5):
buckets = ht_create(bucket_count)
start = time.perf_counter()
for name, phone in records_sorted:
ht_insert(buckets, name, phone)
times.append(time.perf_counter() - start)
results.append(["HashTable", "sorted", "insert", sum(times) / 5] + times)
# Подготовка
buckets = ht_create(bucket_count)
for name, phone in records_shuffled:
ht_insert(buckets, name, phone)
# Поиск
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
ht_find(buckets, name)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "search", sum(times) / 5] + times)
# Удаление
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
buckets_copy = ht_create(bucket_count)
for name, phone in records_shuffled:
ht_insert(buckets_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
ht_delete(buckets_copy, name)
times.append(time.perf_counter() - start)
results.append(["HashTable", "shuffled", "delete", sum(times) / 5] + times)
def test_bst(records_shuffled, records_sorted, results):
N = len(records_shuffled)
# Вставка shuffled
times = []
for _ in range(5):
root = None
start = time.perf_counter()
for name, phone in records_shuffled:
root = bst_insert(root, name, phone)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "insert", sum(times) / 5] + times)
# Вставка sorted (деградация в список)
times = []
for _ in range(5):
root = None
start = time.perf_counter()
for name, phone in records_sorted:
root = bst_insert(root, name, phone)
times.append(time.perf_counter() - start)
results.append(["BST", "sorted", "insert", sum(times) / 5] + times)
# Подготовка
root = None
for name, phone in records_shuffled:
root = bst_insert(root, name, phone)
# Поиск
existing = [f"User_{i:05d}" for i in random.sample(range(N), 100)]
nonexisting = [f"None_{i}" for i in range(10)]
search_names = existing + nonexisting
times = []
for _ in range(5):
start = time.perf_counter()
for name in search_names:
bst_find(root, name)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "search", sum(times) / 5] + times)
# Удаление
delete_names = [f"User_{i:05d}" for i in random.sample(range(N), 50)]
times = []
for _ in range(5):
root_copy = None
for name, phone in records_shuffled:
root_copy = bst_insert(root_copy, name, phone)
start = time.perf_counter()
for name in delete_names:
root_copy = bst_delete(root_copy, name)
times.append(time.perf_counter() - start)
results.append(["BST", "shuffled", "delete", sum(times) / 5] + times)
def save_results(results, filename="docs/data/results.csv"):
import os
os.makedirs("docs/data", exist_ok=True)
with open(filename, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["Structure", "Mode", "Operation", "AvgSec", "Run1", "Run2", "Run3", "Run4", "Run5"])
for row in results:
writer.writerow(row)
print(f"Результаты сохранены в {filename}")
# ============================================================
# 9. MAIN
# ============================================================
def main():
print("Генерация тестовых данных (N=5000 для скорости)...")
shuffled, sorted_records = generate_records(5000)
results = []
print("Тестирование LinkedList...")
test_linked_list(shuffled, sorted_records, results)
print("Тестирование HashTable...")
test_hash_table(shuffled, sorted_records, results)
print("Тестирование BST...")
test_bst(shuffled, sorted_records, results)
save_results(results)
print("\nГотово! Файл results.csv создан.")
print("Можешь построить графики в Excel или Google Sheets.")
if __name__ == "__main__":
main()