diff --git a/1.py b/1.py new file mode 100644 index 0000000..a8344c3 --- /dev/null +++ b/1.py @@ -0,0 +1,79 @@ +# + +import numpy as np +import matplotlib.pyplot as plt + +# Параметры уравнения +alpha = 350.0 # 1/с +omega = 614.4 # рад/с +A = -15.75 # коэф. при cos +B = -8.17 # коэф. при sin +i_pr_val = -43.75 # принуждённая составляющая + +tau = 1 / alpha # ≈ 0.002857 с +t_max = 0.018 # 18 мс (> 6τ) + +# Время +t_before = np.linspace(-0.005, 0, 100) +t_after = np.linspace(0, t_max, 3000) +t_full = np.concatenate([t_before, t_after]) + +# Принуждённая составляющая (постоянна) +i_pr = np.full_like(t_full, i_pr_val) + +# Свободная составляющая +i_sv_before = np.zeros_like(t_before) # до коммутации нет переходного процесса +i_sv_after = np.exp(-alpha * t_after) * (A * np.cos(omega * t_after) + B * np.sin(omega * t_after)) +i_sv_full = np.concatenate([i_sv_before, i_sv_after]) + +# Полный ток +i_full = i_pr + i_sv_full + +# Проверка значений +i0_plus = i_full[len(t_before)] # первое значение при t >= 0 +print(f"i(0+) = {i0_plus:.3f} А (ожидается: {A + i_pr_val:.3f} = {-15.75 - 43.75:.3f} А)") +print(f"i(∞) = {i_pr_val} А") +print(f"i_sv(0+) = {i_sv_full[len(t_before)]:.3f} А (ожидается: {A} А)") + +# Построение +plt.figure(figsize=(13, 7)) + +plt.plot(t_full, i_full, 'b-', linewidth=2.5, label=r'Полный ток $i(t) = i_{\text{пр}} + i_{\text{св}}$') +plt.plot(t_full, i_pr, 'r--', linewidth=1.8, label=rf'Принуждённая: $i_{{\text{{пр}}}} = {i_pr_val}\ \text{{А}}$') +plt.plot(t_full, i_sv_full, 'g-.', linewidth=2.0, label=r'Свободная составляющая $i_{\text{св}}(t)$') + +# Вертикальные линии +plt.axvline(x=0, color='k', linestyle=':', linewidth=1.5, label='Коммутация ($t = 0$)') +plt.axvline(x=3*tau, color='m', linestyle=':', linewidth=1.2, label=rf'$t = 3\tau \approx {3*tau*1000:.1f}\ \text{{мс}}$') + +# Дополнительно: отметим начальное значение +plt.axhline(y=i0_plus, color='orange', linestyle=':', linewidth=1, alpha=0.7) + +# Оформление +plt.axhline(y=0, color='gray', linewidth=0.6, alpha=0.5) +plt.xlabel('Время, с', fontsize=12) +plt.ylabel('Ток $i$, А', fontsize=12) +plt.title( + r'Переходный процесс: $i(t) = (-15.75 \cos 614.4t - 8.17 \sin 614.4t)\, e^{-350t} - 43.75$', + fontsize=13, pad=15 +) +plt.grid(True, alpha=0.3, linestyle='--') +plt.legend(fontsize=10) +plt.xlim(-0.005, t_max) +plt.ylim(-75, 10) # учитываем отрицательный пик ~ -59.5 А + +# Аннотации +plt.text(0.0005, -40, rf'$i(0_+) = {i0_plus:.1f}\ \text{{А}}$', color='b', fontsize=10) +plt.text(0.0005, -62, r'$i_{\text{св}}(0_+) = -15.75\ \text{А}$', color='g', fontsize=10) +plt.text(0.0005, -48, rf'$i_{{\text{{пр}}}} = {i_pr_val}\ \text{{А}}$', color='r', fontsize=10) +plt.text(3*tau + 0.0003, -72, '3τ', color='m', fontsize=9, rotation=90) + +# Подпись до коммутации +plt.text(-0.0045, -65, 'До коммутации:\n$i = 0$, $i_{\\text{св}} = 0$', fontsize=9, color='k') + +plt.tight_layout() +plt.show() +# - + + + + diff --git a/Installation_guide.md b/Installation_guide.md new file mode 100644 index 0000000..3adffa5 --- /dev/null +++ b/Installation_guide.md @@ -0,0 +1,20 @@ +# Установка и настрока Jupiter Lab на Windows (проверено на Windows 11 Home) для дисциплины Алгоритмы и структуры данных +## 1. Скачиваем Anaconda с официального сайта: https://www.anaconda.com/download. +## 2. Заходим в репозиторий dsa на гитхаб https://github.com/askras/dsa и делаем форк его к себе в аккаунт: +![Компьютер](img/1.png) +## 3. В своем аккаунте выбираем репозиторий dsa и копируем ключ SSH: +![Компьютер](img/2.png) +## 4. В командной строке переходим в папку, куда хотим склонировать репозиторий и прописываем команду **git clone “вставляем наш ключ”**. +## 5. Проверяем установлен ли питон командой **python –version**, должны увидеть следующий результат: +![Компьютер](img/3.png) +## (главное чтобы была написана версия питона, цифры могут отличаться, но версия должна быть не ниже 3.13, если цифр нет или текст отличается, то нужно установить питон). +## 6. Заходим в WindowsPowershell и переходим в папку с нашим репозиторием на ПК командой **cd “путь к папке dsa”**. +## 7. Прописываем команду **python -m venv labvenv**, должна появиться папка labvenv в папке dsa: +![Компьютер](img/4.png) +## 8. Переходим в папку labsvenv командой **cd labsvenv** и прописываем команду **.\Scripts\Activate.ps1**. +## 9. Возвращаемся в папку dsa командой **cd “путь к папке dsa”** и прописываем команду **pip install -r requirements.txt**, должно начаться скачивание файлов. +## 10. Пишем команду **jupyter lab** и в браузере должен открыться jupyter lab с нашим репозиторием dsa и возможностью запускать файлы питона: +![Компьютер](img/5.png) +## Чтобы отключить jupyter lab закройте вкладку браузера, в Powershell нажмите Ctrl+C и пропишите команду **deactivate**. + +## Авторы: @telerink и @risullkin diff --git a/img/1.png b/img/1.png new file mode 100644 index 0000000..b6c7f94 Binary files /dev/null and b/img/1.png differ diff --git a/img/2.png b/img/2.png new file mode 100644 index 0000000..1510109 Binary files /dev/null and b/img/2.png differ diff --git a/img/3.png b/img/3.png new file mode 100644 index 0000000..f36c4ac Binary files /dev/null and b/img/3.png differ diff --git a/img/4.png b/img/4.png new file mode 100644 index 0000000..e320593 Binary files /dev/null and b/img/4.png differ diff --git a/img/5.png b/img/5.png new file mode 100644 index 0000000..73292dd Binary files /dev/null and b/img/5.png differ diff --git a/labs/lab_01/Zadanie1.py b/labs/lab_01/Zadanie1.py new file mode 100644 index 0000000..405e982 --- /dev/null +++ b/labs/lab_01/Zadanie1.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +# 1 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + + +# + +# 2 +import random + +N = 6 + +def vector_generator(n): + vec = [random.randint(1, 100*N) for i in range(n)] + return vec +#1.2 +def summa_func(n): + vec = vector_generator(n) + summ = 0 + for num in vec: + summ += num + return summ +#1.3 +def proizvedenie_func(n): + vec = vector_generator(n) + proizv = 1 + for num in vec: + proizv *= num + return proizv +#1.5 +def maximum_func(n): + vec = vector_generator(n) + maxi = 0 + for num in vec: + if num >= maxi: + maxi = num + return maxi +#1.7 +# def srednee_arifm_func(vec): +# vec = vector_generator(n) +# summ = 0 +# lenn = 0 +# for num in vec: +# summ += num +# lenn += 1 +# return summ/lenn +#1.8 +def srednee_garm_func(n): + vec = vector_generator(n) + summ = 0 + lenn = 0 + for num in vec: + summ += num + lenn += 1 + return lenn/summ + + +# + +#3 +import matplotlib.pyplot as plt + +N = 6 + +def five_iteration_summa(n): + time_of_summa = [] + summa_time = get_usage_time(ndigits=5)(summa_func) + for i in range(5): + time_of_summa.append(summa_time(n)) + average_time_summa = sum(time_of_summa)/5 + return average_time_summa + +def five_iteration_proizvedenie(n): + time_of_proizvedenie = [] + proizvedenie_time = get_usage_time(ndigits=5)(proizvedenie_func) + for i in range(5): + time_of_proizvedenie.append(proizvedenie_time(n)) + average_time_proizvedenie = sum(time_of_proizvedenie)/5 + return average_time_proizvedenie + +def five_iteration_maximum(n): + time_of_maximum = [] + maximum_time = get_usage_time(ndigits=5)(maximum_func) + for i in range(5): + time_of_maximum.append(maximum_time(n)) + average_time_maximum = sum(time_of_maximum)/5 + return average_time_maximum + +def five_iteration_srednee(n): + time_of_srednee = [] + srednee_time = get_usage_time(ndigits=5)(srednee_garm_func) + for i in range(5): + time_of_srednee.append(srednee_time(n)) + average_time_srednee = sum(time_of_srednee)/5 + return average_time_srednee + +# %matplotlib inline + +items = range(1, 10**5*N+1, 1000*N) + +times_summa = [] +times_proizvedenie = [] +times_maximum = [] +times_srednee = [] + +j = 0 + +for i in items: + times_summa.append(five_iteration_summa(i)) + times_proizvedenie.append(five_iteration_proizvedenie(i)) + times_maximum.append(five_iteration_maximum(i)) + times_srednee.append(five_iteration_srednee(i)) + if i > 1000: + print('Среднее время произведения для', i, 'элементов:', times_proizvedenie[j]) + j += 1 + +#Summa +plt.figure(figsize=(10, 6)) +plt.plot(items, times_summa) +plt.title('График суммы элементов') +plt.xlabel('Запуски программы') +plt.ylabel('Время, сек') +plt.grid(True) + +#Proizvedenie +plt.figure(figsize=(10, 6)) +plt.plot(items, times_proizvedenie) +plt.title('График произведения элементов') +plt.xlabel('Запуски программы') +plt.ylabel('Время, сек') +plt.grid(True) + +#Maximum +plt.figure(figsize=(10, 6)) +plt.plot(items, times_maximum) +plt.title('График максимума из элементов') +plt.xlabel('Запуски программы') +plt.ylabel('Время, сек') +plt.grid(True) + +#Srednee +plt.figure(figsize=(10, 6)) +plt.plot(items, times_srednee) +plt.title('График среднего гармонического элементов') +plt.xlabel('Запуски программы') +plt.ylabel('Время, сек') +plt.grid(True) +# - + diff --git a/labs/lab_01/Zadanie2.py b/labs/lab_01/Zadanie2.py new file mode 100644 index 0000000..c73e027 --- /dev/null +++ b/labs/lab_01/Zadanie2.py @@ -0,0 +1,83 @@ +# + +# 1 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + + +# + +# 2 +import random + +N = 6 + +def matrix_generator(n): + matrix = [] + for i in range(n): + line = [random.randint(1, 100*N) for j in range(n)] + matrix.append(line) + return matrix + +def matrix_proizv(n): + a = matrix_generator(n) + b = matrix_generator(n) + c = [[] for i in range(n)] + for i in range(n): + for j in range(n): + element = 0 + for k in range(n): + element += a[i][k] * b[k][j] + c[i].append(element) + return c + + +# + +# 3 +import matplotlib.pyplot as plt + +N = 6 + +def five_iteration_matrix(n): + time_of_matrix = [] + matrix_time = get_usage_time(ndigits=5)(matrix_proizv) + for i in range(5): + time_of_matrix.append(matrix_time(n)) + average_time_matrix = sum(time_of_matrix)/5 + return average_time_matrix + +# %matplotlib inline + +items = range(1, 10**2*N+1, 10) +times_matrix = [] +j = 0 +for i in items: + times_matrix.append(five_iteration_matrix(i)) + print('Среднее время произведения матриц для', i, 'порядка матрицы', times_matrix[j]) + j += 1 + +plt.figure(figsize=(10, 6)) +plt.plot(items, times_matrix) +plt.title('График для произведения матриц') +plt.xlabel('Порядок матрицы') +plt.ylabel('Время, сек') +plt.grid(True) +# - + + diff --git a/labs/lab_02/Algoriphm.py b/labs/lab_02/Algoriphm.py new file mode 100644 index 0000000..f6d5d29 --- /dev/null +++ b/labs/lab_02/Algoriphm.py @@ -0,0 +1,214 @@ +# + +# 1 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + + +# + +#2 +import random + +def massiv_generator(n): + massiv = [random.randint(1, 1000) for i in range(n)] + return massiv + + +# + +#3 +import math + +def stooge_sort(n): + return stooge_sort_rec(n, 0, len(n)-1) + +def stooge_sort_rec(n, left, right): + if n[left] > n[right]: + temp = n[left] + n[left] = n[right] + n[right] = temp + + if (right - left + 1) > 2: + part_len = (right - left + 1) // 3 + stooge_sort_rec(n, left, right - part_len) + stooge_sort_rec(n, left + part_len, right) + stooge_sort_rec(n, left, right - part_len) + + return n + +def introsort(n): + length = len(n) + depth = 2 * math.floor(math.log2(length)) + + return introsort_rec(n, 0, length-1, depth) + +def introsort_rec(n, left, right, depth): + size = right - left + 1 + + if size <= 16: + insertion_sort(n, left, right) + return n + + if depth <= 0: + heap_sort(n, left, right) + return n + + pivot_index = median_of_three(n, left, right) + partition_index = partition(n, left, right, pivot_index) + introsort_rec(n, left, partition_index - 1, depth - 1) + introsort_rec(n, partition_index + 1, right, depth - 1) + + return n + +def median_of_three(n, left, right): + mid = left + (right - left) // 2 + + if n[left] > n[mid]: + temp = n[left] + n[left] = n[mid] + n[mid] = temp + if n[left] > n[right]: + temp = n[left] + n[left] = n[right] + n[right] = temp + if n[mid] > n[right]: + temp = n[mid] + n[mid] = n[right] + n[right] = temp + return mid + +def partition(n, left, right, pivot_index): + pivot_value = n[pivot_index] + temp = n[pivot_index] + n[pivot_index] = n[right] + n[right] = temp + + i = left - 1 + + for j in range(left, right): + if n[j] <= pivot_value: + i = i + 1 + temp = n[i] + n[i] = n[j] + n[j] = temp + + temp = n[i+1] + n[i+1] = n[right] + n[right] = temp + + return i + 1 + +def insertion_sort(n, left, right): + for i in range(left + 1, right + 1): + key = n[i] + j = i - 1 + + while j >= left and n[j] > key: + n[j + 1] = n[j] + j = j - 1 + + n[j + 1] = key + +def heap_sort(n, left, right): + index = right - left + 1 + + for i in range(index // 2 - 1, -1, -1): + heapify(n, index, i, left) + + for i in range(index - 1, 0, -1): + temp = n[left] + n[left] = n[left+i] + n[left+i] = temp + + heapify(n, i, 0, left) + +def heapify(n, index, i, left): + largest = i + left_element = 2 * i + 1 + right_element = 2 * i + 2 + + if left_element < index and n[left + left_element] > n[left + largest]: + largest = left_element + + if right_element < index and n[left + right_element] > n[left + largest]: + largest = right_element + + if largest != i: + temp = n[left+i] + n[left+i] = n[largest] + n[largest] = temp + + heapify(n, index, largest, left) + + + +# + +#4 +import matplotlib.pyplot as plt + +n = [1000, 5000, 10_000, 100_000]#[100, 200, 300, 400, 500] + +times_s = [] +times_sort_s = [] +times_reverse_sort_s = [] + +times_i = [] +times_sort_i = [] +times_reverse_sort_i = [] + +for i in n: + massiv = massiv_generator(i) + # time_s = get_usage_time(ndigits=5)(stooge_sort) + # times_s.append(time_s(massiv)) + time_i = get_usage_time(ndigits=5)(introsort) + times_i.append(time_i(massiv)) + + massiv_sort = list(sorted(massiv)) + # time_sort_s = get_usage_time(ndigits=5)(stooge_sort) + # times_sort_s.append(time_sort_s(massiv_sort)) + time_sort_i = get_usage_time(ndigits=5)(introsort) + times_sort_i.append(time_sort_i(massiv_sort)) + + massiv_reverse_sort = list(reversed(massiv_sort)) + # time_reverse_sort_s = get_usage_time(ndigits=5)(stooge_sort) + # times_reverse_sort_s.append(time_reverse_sort_s(massiv_reverse_sort)) + time_reverse_sort_i = get_usage_time(ndigits=5)(introsort) + times_reverse_sort_i.append(time_reverse_sort_i(massiv_reverse_sort)) + +# plt.figure(figsize=(10, 6)) +# plt.plot(n, times_s, label='неотсортированный') +# plt.plot(n, times_sort_s, label='отсортированный') +# plt.plot(n, times_reverse_sort_s, label='отсортированный по убыванию') +# plt.title('График сортировки массива методом stooge_sort') +# plt.xlabel('Количество элементов массива') +# plt.ylabel('Время, сек') +# plt.legend() +# plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_i, label='неотсортированный') +plt.plot(n, times_sort_i, label='отсортированный') +plt.plot(n, times_reverse_sort_i, label='отсортированный по убыванию') +plt.title('График сортировки массива методом introsort') +plt.xlabel('Количество элементов массива') +plt.ylabel('Время, сек') +plt.legend() +plt.grid(True) +# - + diff --git a/labs/lab_02/Lab_02_Shuranov.md b/labs/lab_02/Lab_02_Shuranov.md new file mode 100644 index 0000000..d473d5d --- /dev/null +++ b/labs/lab_02/Lab_02_Shuranov.md @@ -0,0 +1,230 @@ +# Лабораторная работа 2 +# Алгоритмы сортировки +## Цель лабораторной работы +Изучение основных алгоритмов на сортировки. +## Задачи лабораторной работы +1. Общее изучение различных видов алгоритмов сортировки. +2. Подробное изучение алгоритмов сортировки Introsort и Stooge sort. +## Словесная постановка задачи +1. Провести сравнение указанных алгоритмов сортировки массивов, содержащих n1, n2, n3 и n4 элементов. +2. Каждую функцию сортировки вызывать трижды: для сортировки упорядоченного массива, массива, упорядоченного в обратном порядке и неупорядоченного массива. Сортируемая последовательность для всех методов должна быть одинаковой (сортировать копии одного массива). +3. Проиллюстрировать эффективность алгоритмов сортировок по заданному критерию. Построить диаграммы указанных зависимостей. +## реализация алгоритмов stooge sort и introsort +Первая часть программы реализует функцию get_usage_time, позволяющую вычислять время выполнения любой функции: +``` Python +# 1 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator +``` +Вторая часть реализует функцию massiv_generator, которая создает массив случайных чисел длины n +``` Python +#2 +import random + +def massiv_generator(n): + massiv = [random.randint(1, 1000) for i in range(n)] + return massiv +``` +В третьей части происходит сама реализация функций stooge_sort и introsort +``` Python +#3 +import math + +def stooge_sort(n): + return stooge_sort_rec(n, 0, len(n)-1) + +def stooge_sort_rec(n, left, right): + if n[left] > n[right]: + temp = n[left] + n[left] = n[right] + n[right] = temp + + if (right - left + 1) > 2: + part_len = (right - left + 1) // 3 + stooge_sort_rec(n, left, right - part_len) + stooge_sort_rec(n, left + part_len, right) + stooge_sort_rec(n, left, right - part_len) + + return n + +def introsort(n): + length = len(n) + depth = 2 * math.floor(math.log2(length)) + + return introsort_rec(n, 0, length-1, depth) + +def introsort_rec(n, left, right, depth): + size = right - left + 1 + + if size <= 16: + insertion_sort(n, left, right) + return n + + if depth <= 0: + heap_sort(n, left, right) + return n + + pivot_index = median_of_three(n, left, right) + partition_index = partition(n, left, right, pivot_index) + introsort_rec(n, left, partition_index - 1, depth - 1) + introsort_rec(n, partition_index + 1, right, depth - 1) + + return n + +def median_of_three(n, left, right): + mid = left + (right - left) // 2 + + if n[left] > n[mid]: + temp = n[left] + n[left] = n[mid] + n[mid] = temp + if n[left] > n[right]: + temp = n[left] + n[left] = n[right] + n[right] = temp + if n[mid] > n[right]: + temp = n[mid] + n[mid] = n[right] + n[right] = temp + return mid + +def partition(n, left, right, pivot_index): + pivot_value = n[pivot_index] + temp = n[pivot_index] + n[pivot_index] = n[right] + n[right] = temp + + i = left - 1 + + for j in range(left, right): + if n[j] <= pivot_value: + i = i + 1 + temp = n[i] + n[i] = n[j] + n[j] = temp + + temp = n[i+1] + n[i+1] = n[right] + n[right] = temp + + return i + 1 + +def insertion_sort(n, left, right): + for i in range(left + 1, right + 1): + key = n[i] + j = i - 1 + + while j >= left and n[j] > key: + n[j + 1] = n[j] + j = j - 1 + + n[j + 1] = key + +def heap_sort(n, left, right): + index = right - left + 1 + + for i in range(index // 2 - 1, -1, -1): + heapify(n, index, i, left) + + for i in range(index - 1, 0, -1): + temp = n[left] + n[left] = n[left+i] + n[left+i] = temp + + heapify(n, i, 0, left) + +def heapify(n, index, i, left): + largest = i + left_element = 2 * i + 1 + right_element = 2 * i + 2 + + if left_element < index and n[left + left_element] > n[left + largest]: + largest = left + + if right_element < index and n[left + right_element] > n[left + largest]: + largest = right + + if largest != i: + temp = n[left+i] + n[left+i] = n[largest] + n[largest] = temp + + heapify(n, index, largest, left) +``` +Четвертая часть вызывает функции сортировок для различных массивов и рисует графики +``` Python +#4 +import matplotlib.pyplot as plt + +n = [100, 200, 300, 400, 500]#[1000, 5000, 10_000, 100_000] + +times_s = [] +times_sort_s = [] +times_reverse_sort_s = [] + +times_i = [] +times_sort_i = [] +times_reverse_sort_i = [] + +for i in n: + massiv = massiv_generator(i) + time_s = get_usage_time(ndigits=5)(stooge_sort) + times_s.append(time_s(massiv)) + time_i = get_usage_time(ndigits=5)(introsort) + times_i.append(time_i(massiv)) + + massiv_sort = list(sorted(massiv)) + time_sort_s = get_usage_time(ndigits=5)(stooge_sort) + times_sort_s.append(time_sort_s(massiv_sort)) + time_sort_i = get_usage_time(ndigits=5)(introsort) + times_sort_i.append(time_sort_i(massiv_sort)) + + massiv_reverse_sort = list(reversed(massiv_sort)) + time_reverse_sort_s = get_usage_time(ndigits=5)(stooge_sort) + times_reverse_sort_s.append(time_reverse_sort_s(massiv_reverse_sort)) + time_reverse_sort_i = get_usage_time(ndigits=5)(introsort) + times_reverse_sort_i.append(time_reverse_sort_i(massiv_reverse_sort)) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_s, label='неотсортированный') +plt.plot(n, times_sort_s, label='отсоrwртированный') +plt.plot(n, times_reverse_sort_s, label='отсортированный по убыванию') +plt.title('График сортировки массива методом stooge_sort') +plt.xlabel('Количество элементов массива') +plt.ylabel('Время, сек') +plt.legend() +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_i, label='неотсортированный') +plt.plot(n, times_sort_i, label='отсортированный') +plt.plot(n, times_reverse_sort_i, label='отсортированный по убыванию') +plt.title('График сортировки массива методом introsort') +plt.xlabel('Количество элементов массива') +plt.ylabel('Время, сек') +plt.legend() +plt.grid(True) +``` +## Анализ графиков +График для stooge sort +![Компьютер](img2/1.png) +График для introsort +![Компьютер](img2/2.png) diff --git a/labs/lab_02/img2/1.png b/labs/lab_02/img2/1.png new file mode 100644 index 0000000..20d7d56 Binary files /dev/null and b/labs/lab_02/img2/1.png differ diff --git a/labs/lab_02/img2/2.png b/labs/lab_02/img2/2.png new file mode 100644 index 0000000..8ea5242 Binary files /dev/null and b/labs/lab_02/img2/2.png differ diff --git a/labs/lab_02/lab_02.md b/labs/lab_02/lab_02.md index 114089f..7d90be2 100755 --- a/labs/lab_02/lab_02.md +++ b/labs/lab_02/lab_02.md @@ -155,7 +155,6 @@ $f(а_{k_1}) \leqslant f(а_{k_2}) \leqslant ... \leqslant f(а_{k_n})$. 10. Каждую функцию сортировки вызывать трижды: для сортировки упорядоченного массива, массива, упорядоченного в обратном порядке и неупорядоченного массива. Сортируемая последовательность для всех методов должна быть одинаковой (сортировать копии одного массива). 11. Проиллюстрировать эффективность алгоритмов сортировок по заданному критерию. Построить диаграммы указанных зависимостей. - ## Методика и порядок выполнения работы Для успешного выполнения и ащиты лабораторной работ, необходимо выполнить следующие этапы: diff --git a/labs/lab_03/Lab_03_Shuranov.md b/labs/lab_03/Lab_03_Shuranov.md new file mode 100644 index 0000000..e036a52 --- /dev/null +++ b/labs/lab_03/Lab_03_Shuranov.md @@ -0,0 +1,395 @@ +# Лабораторная работа 3 +# Линейные списки (Linked list) +## Цель работы +изучение структуры данных «Линейные списки», а также основных операций над ними. +## Задачи лабораторной работы +1. Изучение вариантов линейных списков +2. Написание односвязного линейного списка +## Словесная постановка задачи +1. Изучить варианты линейных списков +2. Написать 6 версий линейного односвязного списка +3. Написать функцию, которая по двум данным линейным спискам формирует новый список, состоящий из элементов, одновременно входящих в оба данных списка. +## Реализация односвязного списка +Первая часть реализует класс Node, описывающий 1 элемент списка +``` Python +#1 + +class Node: + def __init__(self, data): + self.data = data + self.next = None + + def __repr__(self): + return f"Node({self.data})" +``` +Вторая часть реализует последовательно 6 версий односвязного списка: стандартный, с добавлением указателя на хвост, с подсчетом элементов, с поддержкой итератора, с рекурсией, с втавкой в любое место (метод insert) +``` Python +#2 + +class List1: + def __init__(self): + self.head = None + + def is_empty(self): + return self.head is None + + def delete(self, data): + if self.head is None: + return + if self.head.data == data: + self.head = self.head.next + return + current = self.head + while current.next and current.next.data != data: + current = current.next + if current.next: + current.next = current.next.next + + def display(self): + elements = [] + current = self.head + while current: + elements.append(str(current.data)) + current = current.next + return ", ".join(elements) + ", None" + + def reverse(self): + prev = None + current = self.node + while current: + next_node = current.next + current.next = prev + prev = current + current = next_node + self.head = prev + + def sort(self): + if self.head is None or self.head.next is None: + return + swapped = True + while swapped: + swapped = False + current = self.head + while current and current.next: + if cureent.data > current.next.data: + current.data, current.next.data = current.next.data, current.data + swapped = True + current = current.next + +class List2(List1): + def __init__(self): + super().__init__() + self.tail = None + + def append(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + self.tail = new_node + else: + self.tail.next = new_node + self.tail = new_node + + def prepend(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + self.tail = new_node + else: + new_node.next = self.head + self.head = new_node + +class List3(List2): + def __init__(self): + super().__init__() + self.length = 0 + + def append(self, data): + super().append(data) + self.length += 1 + + def prepend(self, data): + super().prepend(data) + self.length += 1 + + def delete(self, data): + if self.data is None: + return + if self.head.data == data: + self.head = self.head.next + self.length -= 1 + if self.head is None: + self.tail = None + return + current = self.head + while current.next and current.next.data != data: + current = current.next + if current.next: + if current.next == self.tail: + self.tail = current + current.next = current.next.next + self.length -= 1 + +class List4(List3): + def __iter__(self): + current = self.head + while current: + yield current.data + current = current.next + + def __contains__(self, data): + return any(item == data for item in self) + + def __len__(self): + return self.length + +class List5(List4): + def display_recursive(self, node): + if node is None: + return "None" + return f"{node.data} -> {self.display_recursive(node.next)}" + + def display(self): + return self.display_recursive(self.head) + + def reverse_recursive(self, node): + if node is None or node.next is None: + return node + + new_head = self.reverse_recursive(node.next) + node.next.next = node + node.next = None + return new_head + + def reverse(self): + self.head = self.reverse_recursive(self.head) + current = self.head + while current and current.next: + current = current.next + self.tail = current + +class List6(List5): + def __get_item__(self, index): + if index < 0 or index >= self.length: + return Error("Index out of range") + current = self.head + for i in range(index): + current = current.next + return current.data + + def __set_item__(self, index, value): + if index < 0 or index >= self.length: + return Error("Index out of range") + current = self.head + for i in range(index): + current = current.next + curent.data = value + def insert(self, index, data): + if index < 0 or index >= self.length: + return Error("Index out of range") + if index == 0: + self.prepend(data) + elif inex == self.length: + self.append(data) + else: + new_node = Node(data) + current = self.data + for i in range(index - 1): + current = current.next + new_node.next = current.next + current.next = new_node + self.length += 1 +``` +В третьей части происходит реализация индивидуального задания: метода peresechenie, создающего список из общих элементов двух данных списков +``` Python +# 3 +def peresechenie(list1, list2): + result = List6() + current1 = list1.head + + while current1: + current2 = list2.head + found_in_list2 = False + while current2: + if current2.data == current1.data: + found_in_list2 = True + break + current2 = current2.next + if found_in_list2: + result.current = result.head + duplicate = False + while result_current: + if result_current.data == current1.data: + duplicate = True + break + result_current = result_current.next + if not duplicate: + result.append(current1.data) + current1 = current1.next + + return result +``` +В частях 4-10 демонстрируется работа всех основных методов на примерах +``` Python +# 4 +print("List1") +list1 = List1() +list1.append(1) +list1.append(3) +list1.append(2) +print(list1.display()) +list1.reverse() +print(list1.display()) +list1.sort() +print(list1.display()) + +# 5 +print("List2") +list2 = List2() +list2.append(2) +list2.append(3) +list2.prepend(1) +print(list2.display()) + +#6 +print("List3") +list3 = List3() +list3.append(1) +list3.append(2) +list3.append(3) +print(list3.length) +list3.delete(2) +print(list3.length) + +#7 +print("List4") +list4 = List4() +list4.append('a') +list4.append('b') +list4.append('c') +for item in list4: + print(item) +print(f"Содержит 'b': {'b' in list4}") +print(len(list4)) +print() + +#8 +print("List5") +list5 = List5() +list5.append(1) +list5.append(2) +list5.append(3) +print(list5.display()) +list5.reverse() +print(list5.display()) + +#9 +print("List6") +list6 = List6() +list6.append(1) +list6.append(2) +list6.append(3) +list6.insert(1, 100) +print(list6.display()) + +#10 +print("Peresechenie") +lista = List6() +listb = List6() + +lista.append(1) +lista.append(2) +lista.append(3) + +listb.append(1) +listb.append(2) +listb.append(4) + +result = peresechenie(lista, listb) +print(result.display()) +``` +В одиннадцатой части происходит изображение трех основных методов (сортировка, разворот и пресечение) на графиках +``` Python +#11 + +import random +import functools +import timeit +import typing +import matplotlib.pyplot as plt + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + +def list_generator(n): + list0 = List4() + for i in range(n): + list0.append(random.randint(1, 1000)) + return list0 + +def sort(list0): + return list0.sort() + +def reverse(list0): + return list0.reverse() + +times_sort = [] +times_reverse = [] +times_peresechenie = [] + +n = [i for i in range(1000, 10_000, 100)] +for m in n: + list0 = list_generator(m) + list1 = list_generator(m) + + time_sort = get_usage_time(ndigits=5)(sort) + times_sort.append(time_sort(list0)) + + time_reverse = get_usage_time(ndigits=5)(reverse) + times_reverse.append(time_reverse(list0)) + + time_peresechenie = get_usage_time(ndigits=5)(peresechenie) + times_peresechenie.append(time_peresechenie(list0, list1)) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_sort) +plt.title('График сортировки листа') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_reverse) +plt.title('График разворота листа') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_peresechenie) +plt.title('График создания пересечения листов') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) +``` +## Анализ графиков +![Компьютер](img3/1.png) +![Компьютер](img3/2.png) +![Компьютер](img3/3.png) + + +Мы можем заметить, что сортировка пузырьком дает сложность O(n квадрат), переворот и пересечение рабтают O(n), то есть линейно diff --git a/labs/lab_03/List.py b/labs/lab_03/List.py new file mode 100644 index 0000000..e6e43e3 --- /dev/null +++ b/labs/lab_03/List.py @@ -0,0 +1,389 @@ +# + +#1 + +class Node: + def __init__(self, data): + self.data = data + self.next = None + + def __repr__(self): + return f"Node({self.data})" + + +# + +#2 + +class List1: + def __init__(self): + self.head = None + + def is_empty(self): + return self.head is None + + def append(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + return + + current = self.head + while current.next: + current = current.next + current.next = new_node + + def delete(self, data): + if self.head is None: + return + if self.head.data == data: + self.head = self.head.next + return + current = self.head + while current.next and current.next.data != data: + current = current.next + if current.next: + current.next = current.next.next + + def display(self): + elements = [] + current = self.head + while current: + elements.append(str(current.data)) + current = current.next + return ", ".join(elements) + ", None" + + def reverse(self): + prev = None + current = self.head + while current: + next_node = current.next + current.next = prev + prev = current + current = next_node + self.head = prev + + def sort(self): + if self.head is None or self.head.next is None: + return + swapped = True + while swapped: + swapped = False + current = self.head + while current and current.next: + if current.data > current.next.data: + current.data, current.next.data = current.next.data, current.data + swapped = True + current = current.next + +class List2(List1): + def __init__(self): + super().__init__() + self.tail = None + + def append(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + self.tail = new_node + else: + self.tail.next = new_node + self.tail = new_node + + def prepend(self, data): + new_node = Node(data) + if self.head is None: + self.head = new_node + self.tail = new_node + else: + new_node.next = self.head + self.head = new_node + +class List3(List2): + def __init__(self): + super().__init__() + self.length = 0 + + def append(self, data): + super().append(data) + self.length += 1 + + def prepend(self, data): + super().prepend(data) + self.length += 1 + + def delete(self, data): + if self.head is None: + return + if self.head.data == data: + self.head = self.head.next + self.length -= 1 + if self.head is None: + self.tail = None + return + current = self.head + while current.next and current.next.data != data: + current = current.next + if current.next: + if current.next == self.tail: + self.tail = current + current.next = current.next.next + self.length -= 1 + +class List4(List3): + def __iter__(self): + current = self.head + while current: + yield current.data + current = current.next + + def __contains__(self, data): + return any(item == data for item in self) + + def __len__(self): + return self.length + +class List5(List4): + def display_recursive(self, node): + if node is None: + return "None" + return f"{node.data}, {self.display_recursive(node.next)}" + + def display(self): + return self.display_recursive(self.head) + + def reverse_recursive(self, node): + if node is None or node.next is None: + return node + + new_head = self.reverse_recursive(node.next) + node.next.next = node + node.next = None + return new_head + + def reverse(self): + self.head = self.reverse_recursive(self.head) + current = self.head + while current and current.next: + current = current.next + self.tail = current + +class List6(List5): + def __getitem__(self, index): + if index < 0 or index >= self.length: + return Error("Index out of range") + current = self.head + for i in range(index): + current = current.next + return current.data + + def __setitem__(self, index, value): + if index < 0 or index >= self.length: + return Error("Index out of range") + current = self.head + for i in range(index): + current = current.next + curent.data = value + + def insert(self, index, data): + if index < 0 or index >= self.length: + return Error("Index out of range") + if index == 0: + self.prepend(data) + elif index == self.length: + self.append(data) + else: + new_node = Node(data) + current = self.head + for i in range(index - 1): + current = current.next + new_node.next = current.next + current.next = new_node + self.length += 1 + + +# - + +# 3 +def peresechenie(list1, list2): + result = List6() + current1 = list1.head + + while current1: + current2 = list2.head + found_in_list2 = False + while current2: + if current2.data == current1.data: + found_in_list2 = True + break + current2 = current2.next + if found_in_list2: + result_current = result.head + duplicate = False + while result_current: + if result_current.data == current1.data: + duplicate = True + break + result_current = result_current.next + if not duplicate: + result.append(current1.data) + current1 = current1.next + + return result + + +# 4 +print("List1") +list1 = List1() +list1.append(1) +list1.append(3) +list1.append(2) +print(list1.display()) +list1.reverse() +print(list1.display()) +list1.sort() +print(list1.display()) + +# 5 +print("List2") +list2 = List2() +list2.append(2) +list2.append(3) +list2.prepend(1) +print(list2.display()) + +#6 +print("List3") +list3 = List3() +list3.append(1) +list3.append(2) +list3.append(3) +print(list3.length) +list3.delete(2) +print(list3.length) + +#7 +print("List4") +list4 = List4() +list4.append('a') +list4.append('b') +list4.append('c') +for item in list4: + print(item) +print(f"Содержит 'b': {'b' in list4}") +print(len(list4)) +print() + +#8 +print("List5") +list5 = List5() +list5.append(1) +list5.append(2) +list5.append(3) +print(list5.display()) +list5.reverse() +print(list5.display()) + +#9 +print("List6") +list6 = List6() +list6.append(1) +list6.append(2) +list6.append(3) +list6.insert(1, 100) +print(list6.display()) + +# + +#10 +print("Peresechenie") +lista = List6() +listb = List6() + +lista.append(1) +lista.append(2) +lista.append(3) + +listb.append(1) +listb.append(2) +listb.append(4) + +result = peresechenie(lista, listb) +print(result.display()) + +# + +#11 + +import random +import functools +import timeit +import typing +import matplotlib.pyplot as plt + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + +def list_generator(n): + list0 = List4() + for i in range(n): + list0.append(random.randint(1, 1000)) + return list0 + +def sort(list0): + return list0.sort() + +def reverse(list0): + return list0.reverse() + +times_sort = [] +times_reverse = [] +times_peresechenie = [] + +n = [i for i in range(1000, 10_000, 100)] +for m in n: + list0 = list_generator(m) + list1 = list_generator(m) + + time_sort = get_usage_time(ndigits=5)(sort) + times_sort.append(time_sort(list0)) + + time_reverse = get_usage_time(ndigits=5)(reverse) + times_reverse.append(time_reverse(list0)) + + time_peresechenie = get_usage_time(ndigits=5)(peresechenie) + times_peresechenie.append(time_peresechenie(list0, list1)) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_sort) +plt.title('График сортировки листа') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_reverse) +plt.title('График разворота листа') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_peresechenie) +plt.title('График создания пересечения листов') +plt.xlabel('Количество элементов листа') +plt.ylabel('Время, сек') +plt.grid(True) +# - + + diff --git a/labs/lab_03/img3/1.png b/labs/lab_03/img3/1.png new file mode 100644 index 0000000..8b25a64 Binary files /dev/null and b/labs/lab_03/img3/1.png differ diff --git a/labs/lab_03/img3/2.png b/labs/lab_03/img3/2.png new file mode 100644 index 0000000..f9c5800 Binary files /dev/null and b/labs/lab_03/img3/2.png differ diff --git a/labs/lab_03/img3/3.png b/labs/lab_03/img3/3.png new file mode 100644 index 0000000..bbea83a Binary files /dev/null and b/labs/lab_03/img3/3.png differ diff --git a/labs/lab_04/Lab_04_Shuranov.md b/labs/lab_04/Lab_04_Shuranov.md new file mode 100644 index 0000000..f338281 --- /dev/null +++ b/labs/lab_04/Lab_04_Shuranov.md @@ -0,0 +1,399 @@ +# Лабораторная работа 4 +# Стек, Очередь, Дек +## Цель работы +Изучение структур данных стек, очередь, дек, их реализация с помощью массивов и списков. +## Задачи лабораторной работы +1. Изучение структур данных стек, очередь, дек. +2. Реализация стека, очереди, дека двумя способами. +## Словесная постановка задачи +1. Изучить определения стека, очереди, дека. +2. Написать по 2 версии каждой структуры данных. +3. Написать задания, использующие реализованные ранее структуры. +## Реализация односвязного списка +Первая часть реализует базовые класс Stack, Queue, Deque, а также ListNode и DoubleListNode для дальнейшего использования при реализации структур данных +``` Python +#1 +class Stack: + def push(self, x): + raise NotImplementedError + def pop(self): + raise NotImplementedError + def peek(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class Queue: + def enqueue(self, x): + raise NotImplementedError + def dequeue(self): + raise NotImplementedError + def peek(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class Deque: + def pushFront(self, x): + raise NotImplementedError + def pushBack(self, x): + raise NotImplementedError + def popFront(self): + raise NotImplementedError + def popBack(self): + raise NotImplementedError + def peekFront(self): + raise NotImplementedError + def peekBack(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class ListNode: + def __init__(self, data = 0, next_node = None): + self.data = data + self.next = next_node + +class DoubleListNode: + def __init__(self, data=0, prev_node=None, next_node=None): + self.data = data + self.prev = prev_node + self.next = next_node +``` +Вторая часть реализует структуру даннных Stack с помощью массива и односвязного списка +``` Python +#2 +class Massiv_Stack(Stack): + def __init__(self): + self.data = [] + + def push(self, x): + self.data.append(x) + + def pop(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.data.pop() + + def peek(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.data[-1] + def isEmpty(self): + return len(self.data) == 0 + + def __str__(self): + return f"ArrayStack({self.data})" + +class LinkedListStack(Stack): + def __init__(self): + self.head = None + + def push(self, x): + new_node = ListNode(x) + new_node.next = self.head + self.head = new_node + + def pop(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + + data = self.head.data + self.head = self.head.next + return data + + def peek(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.head.data + + def isEmpty(self): + return self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListStack({elements})" +``` +Третья часть реализует структуру даннных Queue с помощью массива и односвязного списка +``` Python +#3 +class Massiv_Queue(Queue): + def __init__(self): + self.data = [] + def enqueue(self, x): + self.data.append(x) + def dequeue(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.data.pop(0) + def peek(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.data[0] + def isEmpty(self): + return len(self.data) == 0 + def __str__(self): + return f"ArrayQueue({self.data})" + +class LinkedListQueue(Queue): + def __init__(self): + self.head = None + self.tail = None + + def enqueue(self, x): + new_node = ListNode(x) + if self.isEmpty(): + self.head = self.tail = new_node + else: + self.tail.next = new_node + self.tail = new_node + + def dequeue(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + data = self.head.data + self.head = self.head.next + if self.head is None: + self.tail = None + return data + + def peek(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.head.data + + def isEmpty(self): + return self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListQueue({elements})" +``` +Четвертая часть реализует структуру даннных Deque с помощью массива и двусвязного списка +``` Python +#4 +class Massiv_Deque(Deque): + def __init__(self): + self.data = [] + + def pushFront(self, x): + self.data.insert(0, x) + + def pushBack(self, x): + self.data.append(x) + + def popFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data.pop(0) + + def popBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data.pop() + + def peekFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data[0] + + def peekBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data[-1] + + def isEmpty(self): + return len(self.data) == 0 + + def __str__(self): + return f"ArrayDeque({self.data})" + +class LinkedListDeque(Deque): + def __init__(self): + self.head = None + self.tail = None + + def pushFront(self, x): + new_node = DoublyListNode(x) + + if self.isEmpty(): + self.head = self.tail = new_node + else: + new_node.next = self.head + self.head.prev = new_node + self.head = new_node + + def pushBack(self, x): + new_node = DoublyListNode(x) + + if self.isEmpty(): + self.head = self.tail = new_node + else: + new_node.prev = self.tail + self.tail.next = new_node + self.tail = new_node + + def popFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + + data = self.head.data + + if self.head == self.tail: + self.head = self.tail = None + else: + self.head = self.head.next + self.head.prev = None + return data + + def popBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + + data = self.tail.data + + if self.head == self.tail: + self.head = self.tail = None + else: + self.tail = self.tail.prev + self.tail.next = None + return data + + def peekFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.head.data + + def peekBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.tail.data + + def isEmpty(self): + raise self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListDeque({elements})" +``` +Пятая часть реализует функцию brackets_check, проверяющую наличие пары у всех скобок в строке через стек +``` Python +#5 +def brackets_check(n): + stack = Massiv_Stack() + + bracket_pairs = {'(': ')', '[': ']', '{': '}'} + + for i, char in enumerate(n): + if char in bracket_pairs: + stack.push(char) + elif char in bracket_pairs.values(): + if stack.isEmpty(): + return False + + last_open = stack.pop() + + if bracket_pairs[last_open] != char: + return False + + if not stack.isEmpty(): + return False + + return True + +n = '[[[]]]' +print(brackets_check(n)) +``` +Шестая часть реализует функцию polish_calculator, вычисляющую значение выражения в постфиксной записи +``` Python +#6 +def polish_calculator(n): + stack = Massiv_Stack() + + for i, char in enumerate(n): + if char.isdigit(): + stack.push(int(char)) + else: + b = stack.pop() + a = stack.pop() + + if char == '+': + result = a + b + elif char == '-': + result = a - b + elif char == '*': + result = a * b + elif char == '/': + if b == 0: + raise ZeroDivisionError("Деление на ноль") + result = a / b + + stack.push(result) + + final_result = stack.pop() + + return final_result + +n = '123+4*+' +print(polish_calculator(n)) +``` +Седьмая часть реализует функцию inf_to_polish, переводящую выражения из инфиксной в постфиксную формы записи +``` Python +#7 +def inf_to_polish(n): + operators = {'+': 1, '-': 1, '*': 2, '/': 2,'(': 0} + + result = [] + stack = Massiv_Stack() + + expression = n.replace('(', ' ( ').replace(')', ' ) ') + expression = expression.replace('+', ' + ').replace('-', ' - ') + expression = expression.replace('*', ' * ').replace('/', ' / ') + + chars = expression.split() + + for char in chars: + if char.isdigit() or char.isalpha(): + result.append(char) + + elif char == '(': + stack.push(char) + + elif char == ')': + while not stack.isEmpty() and stack.peek() != '(': + result.append(stack.pop()) + if stack.isEmpty(): + raise ValueError("Несбалансированные скобки") + + stack.pop() + + elif char in operators: + while (not stack.isEmpty() and stack.peek() != '(' and operators[stack.peek()] >= operators[char]): + result.append(stack.pop()) + + stack.push(char) + + while not stack.isEmpty(): + if stack.peek() == '(': + raise ValueError("Несбалансированные скобки") + result.append(stack.pop()) + + final_result = ' '.join(result) + + return final_result + +n = 'a+(b+c)*d' +print(inf_to_polish(n)) +``` diff --git a/labs/lab_04/lab4.py b/labs/lab_04/lab4.py new file mode 100644 index 0000000..0ab0bbc --- /dev/null +++ b/labs/lab_04/lab4.py @@ -0,0 +1,388 @@ +# + +#1 +class Stack: + def push(self, x): + raise NotImplementedError + def pop(self): + raise NotImplementedError + def peek(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class Queue: + def enqueue(self, x): + raise NotImplementedError + def dequeue(self): + raise NotImplementedError + def peek(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class Deque: + def pushFront(self, x): + raise NotImplementedError + def pushBack(self, x): + raise NotImplementedError + def popFront(self): + raise NotImplementedError + def popBack(self): + raise NotImplementedError + def peekFront(self): + raise NotImplementedError + def peekBack(self): + raise NotImplementedError + def isEmpty(self): + raise NotImplementedError + +class ListNode: + def __init__(self, data = 0, next_node = None): + self.data = data + self.next = next_node + +class DoubleListNode: + def __init__(self, data=0, prev_node=None, next_node=None): + self.data = data + self.prev = prev_node + self.next = next_node + + +# + +#2 +class Massiv_Stack(Stack): + def __init__(self): + self.data = [] + + def push(self, x): + self.data.append(x) + + def pop(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.data.pop() + + def peek(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.data[-1] + def isEmpty(self): + return len(self.data) == 0 + + def __str__(self): + return f"ArrayStack({self.data})" + +class LinkedListStack(Stack): + def __init__(self): + self.head = None + + def push(self, x): + new_node = ListNode(x) + new_node.next = self.head + self.head = new_node + + def pop(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + + data = self.head.data + self.head = self.head.next + return data + + def peek(self): + if self.isEmpty(): + raise IndexError("Stack is empty") + return self.head.data + + def isEmpty(self): + return self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListStack({elements})" + + +# + +#3 +class Massiv_Queue(Queue): + def __init__(self): + self.data = [] + def enqueue(self, x): + self.data.append(x) + def dequeue(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.data.pop(0) + def peek(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.data[0] + def isEmpty(self): + return len(self.data) == 0 + def __str__(self): + return f"ArrayQueue({self.data})" + +class LinkedListQueue(Queue): + def __init__(self): + self.head = None + self.tail = None + + def enqueue(self, x): + new_node = ListNode(x) + if self.isEmpty(): + self.head = self.tail = new_node + else: + self.tail.next = new_node + self.tail = new_node + + def dequeue(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + data = self.head.data + self.head = self.head.next + if self.head is None: + self.tail = None + return data + + def peek(self): + if self.isEmpty(): + raise IndexError("Queue is empty") + return self.head.data + + def isEmpty(self): + return self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListQueue({elements})" + + +# + +#4 +class Massiv_Deque(Deque): + def __init__(self): + self.data = [] + + def pushFront(self, x): + self.data.insert(0, x) + + def pushBack(self, x): + self.data.append(x) + + def popFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data.pop(0) + + def popBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data.pop() + + def peekFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data[0] + + def peekBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.data[-1] + + def isEmpty(self): + return len(self.data) == 0 + + def __str__(self): + return f"ArrayDeque({self.data})" + +class LinkedListDeque(Deque): + def __init__(self): + self.head = None + self.tail = None + + def pushFront(self, x): + new_node = DoublyListNode(x) + + if self.isEmpty(): + self.head = self.tail = new_node + else: + new_node.next = self.head + self.head.prev = new_node + self.head = new_node + + def pushBack(self, x): + new_node = DoublyListNode(x) + + if self.isEmpty(): + self.head = self.tail = new_node + else: + new_node.prev = self.tail + self.tail.next = new_node + self.tail = new_node + + def popFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + + data = self.head.data + + if self.head == self.tail: + self.head = self.tail = None + else: + self.head = self.head.next + self.head.prev = None + return data + + def popBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + + data = self.tail.data + + if self.head == self.tail: + self.head = self.tail = None + else: + self.tail = self.tail.prev + self.tail.next = None + return data + + def peekFront(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.head.data + + def peekBack(self): + if self.isEmpty(): + raise IndexError("Deque is empty") + return self.tail.data + + def isEmpty(self): + raise self.head is None + + def __str__(self): + elements = [] + current = self.head + while current: + elements.append(current.data) + current = current.next + return f"LinkedListDeque({elements})" + + +# + +#5 +def brackets_check(n): + stack = Massiv_Stack() + + bracket_pairs = {'(': ')', '[': ']', '{': '}'} + + for i, char in enumerate(n): + if char in bracket_pairs: + stack.push(char) + elif char in bracket_pairs.values(): + if stack.isEmpty(): + return False + + last_open = stack.pop() + + if bracket_pairs[last_open] != char: + return False + + if not stack.isEmpty(): + return False + + return True + +n = '[[[]]]' +print(brackets_check(n)) + + +# + +#6 +def polish_calculator(n): + stack = Massiv_Stack() + + for i, char in enumerate(n): + if char.isdigit(): + stack.push(int(char)) + else: + b = stack.pop() + a = stack.pop() + + if char == '+': + result = a + b + elif char == '-': + result = a - b + elif char == '*': + result = a * b + elif char == '/': + if b == 0: + raise ZeroDivisionError("Деление на ноль") + result = a / b + + stack.push(result) + + final_result = stack.pop() + + return final_result + +n = '123+4*+' +print(polish_calculator(n)) + + +# + +#7 +def inf_to_polish(n): + operators = {'+': 1, '-': 1, '*': 2, '/': 2,'(': 0} + + result = [] + stack = Massiv_Stack() + + expression = n.replace('(', ' ( ').replace(')', ' ) ') + expression = expression.replace('+', ' + ').replace('-', ' - ') + expression = expression.replace('*', ' * ').replace('/', ' / ') + + chars = expression.split() + + for char in chars: + if char.isdigit() or char.isalpha(): + result.append(char) + + elif char == '(': + stack.push(char) + + elif char == ')': + while not stack.isEmpty() and stack.peek() != '(': + result.append(stack.pop()) + if stack.isEmpty(): + raise ValueError("Несбалансированные скобки") + + stack.pop() + + elif char in operators: + while (not stack.isEmpty() and stack.peek() != '(' and operators[stack.peek()] >= operators[char]): + result.append(stack.pop()) + + stack.push(char) + + while not stack.isEmpty(): + if stack.peek() == '(': + raise ValueError("Несбалансированные скобки") + result.append(stack.pop()) + + final_result = ' '.join(result) + + return final_result + +n = 'a+(b+c)*d' +print(inf_to_polish(n)) +# - + + diff --git a/labs/lab_05/Lab_05_Shuranov.md b/labs/lab_05/Lab_05_Shuranov.md new file mode 100644 index 0000000..792628a --- /dev/null +++ b/labs/lab_05/Lab_05_Shuranov.md @@ -0,0 +1,272 @@ +# Лабораторная работа 5 +# Хеш-функции и хеш-таблицы +## Цель работы +Изучение структур данных хэш-таблица и различных способов разрешения коллизии +## Задачи лабораторной работы +1. Изучение структур данных хэш-таблица +2. Реализация хэш-таблиц на основе метода цепочек +3. Реализация хэш-таблиц на основе открытой адресации +## Словесная постановка задачи +1. Изучить определения хэш-таблиц и способов разрешения коллизии +2. Написать 2 реализации хэш-таблиц +3. Написать задания, использующие реализованные ранее структуры +## Реализация хэш-таблиц +Первая часть реализует класс Node, задающий отдельную ноду хэш-таблицы +``` Python +#1 +class Node: + def __init__(self, key, value): + self.key = key + self.value = value + self.next = None +``` +Вторая часть реализует хэш-таблицы на основе метода цепочек +``` Python +#2 +class HashTableChain: + def __init__(self, capacity): + self.capacity = capacity + self.size = 0 + self.table = [None] * capacity + + def _hash(self, key): + return hash(key) % self.capacity + + def insert(self, key, value): + index = self._hash(key) + + if self.table[index] is None: + self.table[index] = Node(key, value) + self.size += 1 + else: + current = self.table[index] + while current: + if current.key == key: + current.value = value + return + current = current.next + new_node = Node(key, value) + new_node.next = self.table[index] + self.table[index] = new_node + self.size += 1 + + def get(self, key): + index = self._hash(key) + + current = self.table[index] + while current: + if current.key == key: + return current.value + current = current.next + + raise KeyError(key) + + def delete(self, key): + index = self._hash(key) + + previous = None + current = self.table[index] + + while current: + if current.key == key: + if previous: + previous.next = current.next + else: + self.table[index] = current.next + self.size -= 1 + return + previous = current + current = current.next + + raise KeyError(key) + + def __len__(self): + return self.size + + def __contains__(self, key): + try: + self.get(key) + return True + except KeyError: + return False + + def __str__(self): + elements = [] + for i in range(self.capacity): + current = self.table[i] + while current: + elements.append((current.key, current.value)) + current = current.next + return str(elements) +``` +Третья часть реализует хэш-таблицы на основе открытой адресации +``` Python +#3 +class HashTableOpenAddress: + def __init__(self, capacity): + self.capacity = capacity + self.size = 0 + self.table = [None] * capacity + self.DELETED = object() + + def _hash(self, key): + return hash(key) % self.capacity + + def _probe(self, key, attempt): + return (self._hash(key) + attempt) % self.capacity + + def insert(self, key, value): + if self.size >= self.capacity: + raise Exception("Hash table is full") + + for attempt in range(self.capacity): + index = self._probe(key, attempt) + + if self.table[index] is None or self.table[index] == self.DELETED: + self.table[index] = Node(key, value) + self.size += 1 + return + elif self.table[index].key == key: + self.table[index].value = value + return + + raise Exception("Hash table is full") + + def get(self, key): + for attempt in range(self.capacity): + index = self._probe(key, attempt) + if self.table[index] is None: + raise KeyError(key) + if self.table[index] == self.DELETED: + continue + if self.table[index].key == key: + return self.table[index].value + + raise KeyError(key) + + def delete(self, key): + for attempt in range(self.capacity): + index = self._probe(key, attempt) + + if self.table[index] is None: + raise KeyError(key) + + if self.table[index] == self.DELETED: + continue + + if self.table[index].key == key: + self.table[index] = self.DELETED + self.size -= 1 + return + + raise KeyError(key) + + def __len__(self): + return self.size + + def __contains__(self, key): + try: + self.get(key) + return True + except KeyError: + return False + + def __str__(self): + result = [] + for i, item in enumerate(self.table): + if item is None: + result.append(f"{i}: None") + elif item == self.DELETED: + result.append(f"{i}: DELETED") + else: + result.append(f"{i}: ({item.key}, {item.value})") + return "\n".join(result) +``` +Четвертая часть реализует функцию peresechenie, проверяющую существование общих элементов в 2х массивах +``` Python +#4 +def peresechenie(a, b): + hash_table = HashTableChain(len(a)) + + for element in a: + hash_table.insert(element, True) + + for element in b: + if element in hash_table: + return True + + return False + +a, b = [1, 2, 3, 4], [1, 5, 6] +print(peresechenie(a,b)) +``` +Пятая часть реализует функцию unique_el, проверяющую уникальны ли элементы в массиве +``` Python +#5 +def unique_el(n): + hash_table = HashTableChain(len(n)) + + for element in n: + if element in hash_table: + return False + + hash_table.insert(element, True) + + return True + +n = [1,2,3,4] +print(unique_el(n)) +``` +Шестая часть реализует функцию pairs_search, находящую пары чисел в массиве с заданной суммой +``` Python +#6 +def pairs_search(n, s): + pairs = [] + hash_table = HashTableChain(len(n)) + + for n1 in n: + n2 = s - n1 + + if n2 in hash_table: + pairs.append((n2, n1)) + + hash_table.insert(n1, True) + + return pairs + +n = [1,2,3,4] +s = 5 +print(pairs_search(n, s)) +``` +Седьмая часть реализует функцию anagrams, проверяющую являются ли две строки анаграммами +``` Python +#7 +def anagrams(n1, n2): + if len(n1) != len(n2): + return False + + char_count = HashTableChain(len(n1)) + + for char in n1: + if char in char_count: + current_count = char_count.get(char) + char_count.insert(char, current_count + 1) + else: + char_count.insert(char, 1) + + for char in n2: + if char not in char_count: + return False + + current_count = char_count.get(char) + if current_count == 1: + char_count.delete(char) + else: + char_count.insert(char, current_count - 1) + + return len(char_count) == 0 + +n1, n2 = '1234', '4322' +print(anagrams(n1, n2)) +``` +На основании работы данных функций мы можем сделать вывод, что хэш-таблицы хорошо подходят для выполнения специфических заданий при анализе массивов и строк. diff --git a/labs/lab_05/lab5.py b/labs/lab_05/lab5.py new file mode 100644 index 0000000..823b6d1 --- /dev/null +++ b/labs/lab_05/lab5.py @@ -0,0 +1,256 @@ +#1 +class Node: + def __init__(self, key, value): + self.key = key + self.value = value + self.next = None + + +#2 +class HashTableChain: + def __init__(self, capacity): + self.capacity = capacity + self.size = 0 + self.table = [None] * capacity + + def _hash(self, key): + return hash(key) % self.capacity + + def insert(self, key, value): + index = self._hash(key) + + if self.table[index] is None: + self.table[index] = Node(key, value) + self.size += 1 + else: + current = self.table[index] + while current: + if current.key == key: + current.value = value + return + current = current.next + new_node = Node(key, value) + new_node.next = self.table[index] + self.table[index] = new_node + self.size += 1 + + def get(self, key): + index = self._hash(key) + + current = self.table[index] + while current: + if current.key == key: + return current.value + current = current.next + + raise KeyError(key) + + def delete(self, key): + index = self._hash(key) + + previous = None + current = self.table[index] + + while current: + if current.key == key: + if previous: + previous.next = current.next + else: + self.table[index] = current.next + self.size -= 1 + return + previous = current + current = current.next + + raise KeyError(key) + + def __len__(self): + return self.size + + def __contains__(self, key): + try: + self.get(key) + return True + except KeyError: + return False + + def __n__(self): + elements = [] + for i in range(self.capacity): + current = self.table[i] + while current: + elements.append((current.key, current.value)) + current = current.next + return n(elements) + + +#3 +class HashTableOpenAddress: + def __init__(self, capacity): + self.capacity = capacity + self.size = 0 + self.table = [None] * capacity + self.DELETED = object() + + def _hash(self, key): + return hash(key) % self.capacity + + def _probe(self, key, attempt): + return (self._hash(key) + attempt) % self.capacity + + def insert(self, key, value): + if self.size >= self.capacity: + raise Exception("Hash table is full") + + for attempt in range(self.capacity): + index = self._probe(key, attempt) + + if self.table[index] is None or self.table[index] == self.DELETED: + self.table[index] = Node(key, value) + self.size += 1 + return + elif self.table[index].key == key: + self.table[index].value = value + return + + raise Exception("Hash table is full") + + def get(self, key): + for attempt in range(self.capacity): + index = self._probe(key, attempt) + if self.table[index] is None: + raise KeyError(key) + if self.table[index] == self.DELETED: + continue + if self.table[index].key == key: + return self.table[index].value + + raise KeyError(key) + + def delete(self, key): + for attempt in range(self.capacity): + index = self._probe(key, attempt) + + if self.table[index] is None: + raise KeyError(key) + + if self.table[index] == self.DELETED: + continue + + if self.table[index].key == key: + self.table[index] = self.DELETED + self.size -= 1 + return + + raise KeyError(key) + + def __len__(self): + return self.size + + def __contains__(self, key): + try: + self.get(key) + return True + except KeyError: + return False + + def __n__(self): + result = [] + for i, item in enumerate(self.table): + if item is None: + result.append(f"{i}: None") + elif item == self.DELETED: + result.append(f"{i}: DELETED") + else: + result.append(f"{i}: ({item.key}, {item.value})") + return "\n".join(result) + + +# + +#4 +def peresechenie(a, b): + hash_table = HashTableChain(len(a)) + + for element in a: + hash_table.insert(element, True) + + for element in b: + if element in hash_table: + return True + + return False + +a, b = [1, 2, 3, 4], [1, 5, 6] +print(peresechenie(a,b)) + + +# + +#5 +def unique_el(n): + hash_table = HashTableChain(len(n)) + + for element in n: + if element in hash_table: + return False + + hash_table.insert(element, True) + + return True + +n = [1,2,3,4] +print(unique_el(n)) + + +# + +#6 +def pairs_search(n, s): + pairs = [] + hash_table = HashTableChain(len(n)) + + for n1 in n: + n2 = s - n1 + + if n2 in hash_table: + pairs.append((n2, n1)) + + hash_table.insert(n1, True) + + return pairs + +n = [1,2,3,4] +s = 5 +print(pairs_search(n, s)) + + +# + +#7 +def anagrams(n1, n2): + if len(n1) != len(n2): + return False + + char_count = HashTableChain(len(n1)) + + for char in n1: + if char in char_count: + current_count = char_count.get(char) + char_count.insert(char, current_count + 1) + else: + char_count.insert(char, 1) + + for char in n2: + if char not in char_count: + return False + + current_count = char_count.get(char) + if current_count == 1: + char_count.delete(char) + else: + char_count.insert(char, current_count - 1) + + return len(char_count) == 0 + +n1, n2 = '1234', '4322' +print(anagrams(n1, n2)) +# - + + diff --git a/labs/lab_06/Lab_06_Shuranov.md b/labs/lab_06/Lab_06_Shuranov.md new file mode 100644 index 0000000..ad3987f --- /dev/null +++ b/labs/lab_06/Lab_06_Shuranov.md @@ -0,0 +1,183 @@ +# Лабораторная работа 6 +# Итеративные и рекурсивные алгоритмы +## Цель работы +Изучение рекурсивных алгоритмов и применение их для написания своего рекурсивного алгоритма. +## Задачи лабораторной работы +1. Изучение понятий рекурсивных алгоритмов +2. Реализация индивидуального алгоритма с помощью рекурсии +3. Реализация индивидуального алгоритма с помощью итераций +## Словесная постановка задачи +1. Изучить определения рекурсивных алгоритмов +2. Написать 2 реализации индивидуального задания +3. Построить графики зависимости выполнения рекурсивной функции от длины получающегося массива +## Реализация рекурсивной функции +Первая часть реализует выполнения функции posl_founder1 с помощью рекурсии (мы постепенно строим увеличивающиеся числовые последовательности длины n) +``` Python +#1 +def posl_founder1(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + + def recursion(posl, bol): + if len(posl) == n: + result = [] + for i in posl: + result.append(i) + posledovatelnosti.append(result) + return + for i in range(bol, k+1): + posl.append(i) + recursion(posl, i+1) + posl.pop() + + recursion([], 1) + + return posledovatelnosti + +print(posl_founder2(4, 3)) +``` +Вторая часть реализует аналогичную функцию posl_founder2 без использования рекурсии (через while) +``` Python +#2 +def posl_founder2(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + stack = [([], 1)] + + while stack: + posl, bol = stack.pop() + + if len(posl) == n: + posledovatelnosti.append(posl) + for i in range(bol, k+1): + new_posl = posl + [i] + stack.append((new_posl, i+1)) + + return posledovatelnosti + +print(posl_founder2(2, 3)) +``` +Третья часть реализует рекурсивную функцию с мемоизацией +``` Python +#3 +def posl_founder3(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + cache = {} + + def recursion(posl, bol): + current_len = len(posl) + if current_len == n: + posledovatelnosti.append(posl[:]) + return + + key = (current_len, bol) + if key in cache: + for continuee in cache[key]: + full_posl = posl + continuee + posledovatelnosti.append(full_posl) + return + + continuations = [] + original_len = len(posledovatelnosti) + + for i in range(bol, k+1): + posl.append(i) + recursion(posl, i+1) + posl.pop() + + new_sequences = posledovatelnosti[original_len:] + for seq in new_sequences: + continuee = seq[current_len:] + if continuee not in continuations: + continuations.append(continuee) + + cache[key] = continuations + + recursion([], 1) + return posledovatelnosti + +print(posl_founder3(2, 3)) +``` +Четвертая часть программы реализует функцию get_usage_time, позволяющую вычислять время выполнения любой функции +``` Python +#4 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator +``` +Пятая часть реализует отображение графиков зависимости времени выполнения всех функций от различных вводных +``` Python +#5 +import matplotlib.pyplot as plt + +def f1(i): + n, k = i, i+1 + return posl_founder1(n, k) +def f2(i): + n, k = i, i+1 + return posl_founder2(n, k) +def f3(i): + n, k = i, i+1 + return posl_founder3(n, k) + +times_f1, times_f2, times_f3 = [], [], [] +n = [i for i in range(1, 23)] + +for i in n: + time_f1 = get_usage_time(ndigits=5)(f1) + times_f1.append(time_f1(i)) + + time_f2 = get_usage_time(ndigits=5)(f2) + times_f2.append(time_f2(i)) + + time_f3 = get_usage_time(ndigits=5)(f3) + times_f3.append(time_f3(i)) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f1) +plt.title('График рекурсивного выполнения функции') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f2) +plt.title('График итеративного выполнения функции') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f1) +plt.title('График рекурсивного выполнения функции с мемоизацией') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) +``` +Проанализируем графики + + +![Компьютер](img6/1.png) +![Компьютер](img6/2.png) +![Компьютер](img6/3.png) diff --git a/labs/lab_06/img6/1.png b/labs/lab_06/img6/1.png new file mode 100644 index 0000000..fcaae15 Binary files /dev/null and b/labs/lab_06/img6/1.png differ diff --git a/labs/lab_06/img6/2.png b/labs/lab_06/img6/2.png new file mode 100644 index 0000000..39849f8 Binary files /dev/null and b/labs/lab_06/img6/2.png differ diff --git a/labs/lab_06/img6/3.png b/labs/lab_06/img6/3.png new file mode 100644 index 0000000..df4141d Binary files /dev/null and b/labs/lab_06/img6/3.png differ diff --git a/labs/lab_06/lab6.py b/labs/lab_06/lab6.py new file mode 100644 index 0000000..79532d8 --- /dev/null +++ b/labs/lab_06/lab6.py @@ -0,0 +1,163 @@ +# + +#1 +def posl_founder1(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + + def recursion(posl, bol): + if len(posl) == n: + result = [] + for i in posl: + result.append(i) + posledovatelnosti.append(result) + return + for i in range(bol, k+1): + posl.append(i) + recursion(posl, i+1) + posl.pop() + + recursion([], 1) + + return posledovatelnosti + +print(posl_founder1(2, 3)) + + +# + +#2 +def posl_founder2(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + stack = [([], 1)] + + while stack: + posl, bol = stack.pop() + + if len(posl) == n: + posledovatelnosti.append(posl) + for i in range(bol, k+1): + new_posl = posl + [i] + stack.append((new_posl, i+1)) + + return posledovatelnosti + +print(posl_founder2(2, 3)) + + +# + +#3 +def posl_founder3(n, k): + if n > k: + raise Exception("n > k") + posledovatelnosti = [] + cache = {} + + def recursion(posl, bol): + current_len = len(posl) + if current_len == n: + posledovatelnosti.append(posl[:]) + return + + key = (current_len, bol) + if key in cache: + for continuee in cache[key]: + full_posl = posl + continuee + posledovatelnosti.append(full_posl) + return + + continuations = [] + original_len = len(posledovatelnosti) + + for i in range(bol, k+1): + posl.append(i) + recursion(posl, i+1) + posl.pop() + + new_sequences = posledovatelnosti[original_len:] + for seq in new_sequences: + continuee = seq[current_len:] + if continuee not in continuations: + continuations.append(continuee) + + cache[key] = continuations + + recursion([], 1) + return posledovatelnosti + +print(posl_founder3(2, 3)) + +# + +#4 +import functools +import timeit +import typing + +def get_usage_time( + *, number: int = 1, setup: str = 'pass', ndigits: int = 3 +) -> typing.Callable: + def decorator(func: typing.Callable) -> typing.Callable: + @functools.wraps(func) + def wrapper(*args, **kwargs) -> float: + usage_time = timeit.timeit( + lambda: func(*args, **kwargs), + setup=setup, + number=number, + ) + return round(usage_time / number, ndigits) + + return wrapper + + return decorator + + +# + +#5 +import matplotlib.pyplot as plt + +def f1(i): + n, k = i, i+1 + return posl_founder1(n, k) +def f2(i): + n, k = i, i+1 + return posl_founder2(n, k) +def f3(i): + n, k = i, i+1 + return posl_founder3(n, k) + +times_f1, times_f2, times_f3 = [], [], [] +n = [i for i in range(1, 23)] + +for i in n: + time_f1 = get_usage_time(ndigits=5)(f1) + times_f1.append(time_f1(i)) + + time_f2 = get_usage_time(ndigits=5)(f2) + times_f2.append(time_f2(i)) + + time_f3 = get_usage_time(ndigits=5)(f3) + times_f3.append(time_f3(i)) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f1) +plt.title('График рекурсивного выполнения функции') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f2) +plt.title('График итеративного выполнения функции') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) + +plt.figure(figsize=(10, 6)) +plt.plot(n, times_f1) +plt.title('График рекурсивного выполнения функции с мемоизацией') +plt.xlabel('Длина последовательности') +plt.ylabel('Время, сек') +plt.grid(True) +# - + diff --git a/labs/lab_07/Lab_07_Shuranov.md b/labs/lab_07/Lab_07_Shuranov.md new file mode 100644 index 0000000..970a831 --- /dev/null +++ b/labs/lab_07/Lab_07_Shuranov.md @@ -0,0 +1,258 @@ +# Лабораторная работа 7 +# Алгоритмы на графах +## Цель работы +Изучение основных алгоритмов на графах. +## Задачи лабораторной работы +1. Изучение основных алгоритмов на графах +2. Реализация класса граф и различных алгоритмов его обхода +3. Применение алгоритомв для решения задачи +## Словесная постановка задачи +1. Изучить как происходит обход графа +2. Реализовать свой класс граф и оболочку для пользователя +3. Применить этот класс для решения задачи +## Реализация алгоритмов на графах +Первая часть реализует класс Graph +``` Python +#1 +class Graph: + def __init__(self, vershini=None): + if vershini: + self.vershini = vershini + else: + self.vershini = [] + self.spisok_smeznosti = {vershina: [] for vershina in self.vershini} + self.vesa_reber = {} + + def add_vershina(self, vershina): + if vershina not in self.spisok_smeznosti: + self.vershini.append(vershina) + self.spisok_smeznosti[vershina] = [] + def add_rebro(self, u, v, ves): + if u not in self.spisok_smeznosti: + self.add_vershina(u) + if v not in self.spisok_smeznosti: + self.add_vershina(v) + if v not in self.spisok_smeznosti[u]: + self.spisok_smeznosti[u].append(v) + if u not in self.spisok_smeznosti[v]: + self.spisok_smeznosti[v].append(u) + self.vesa_reber[(u, v)] = ves + self.vesa_reber[(v, u)] = ves + + def delete_vershina(self, vershina): + for sosed in self.spisok_smeznosti[vershina]: + self.spisok_smeznosti[vershina].remove(vershina) + if (vershina, sosed) in self.vesa_reber: + del self.vesa_reber[(vershina, sosed)] + if (sosed, vershina) in self.vesa_reber: + del self.vesa_reber[(sosed, vershina)] + del self.spisok_smeznosti[vershina] + self.vershini.remove(vershina) + def delete_rebro(self, u, v): + self.spisok_smeznosti[u].remove(v) + self.spisok_smeznosti[v].remove(u) + if (u, v) in self.vesa_reber: + del self.vesa_reber[(u, v)] + if (v, u) in self.vesa_reber: + del self.vesa_reber[(v, u)] + + def get_vershini(self): + return self.vershini + def get_stepen_vershini(self, vershina): + return len(self.spisok_smeznosti[vershina]) + def get_rebra(self): + rebra = [] + visited_rebra = set() + + for u in self.spisok_smeznosti: + for v in self.spisok_smeznosti[u]: + rebro = tuple(sorted((u, v))) + if rebro not in visited_rebra: + rebra.append((u, v)) + visited_rebra.add(rebro) + return rebra + def get_ves_rebra(self, u, v): + return self.vesa_reber.get((u, v), 1) + + def set_ves_rebra(self, u, v, ves): + self.vesa_reber[(u, v)] = ves + self.vesa_reber[(v, u)] = ves + + def display(self): + print("Вершины:", self.vershini) + print("Рёбра:") + rebra = self.get_rebra() + for rebro in rebra: + print(f" {rebro[0]} - {rebro[1]}") + print("\nСписок смежности:") + for vershina in sorted(self.vershini): + print(f" {vershina}: {sorted(self.spisok_smeznosti[vershina])}") + +a = Graph() +a.add_vershina('b') +print(a.display()) +``` +Вторая часть реализует обход графа в глубину +``` Python +#2 +def obhod_v_glubinu(graph, start): + visited = set() + result = [] + stack = [start] + + while stack: + vershina = stack.pop() + if vershina not in visited: + visited.add(vershina) + result.append(vershina) + for sosed in reversed(graph.spisok_smeznosti[vershina]): + if sosed not in visited: + stack.append(sosed) + return result +``` +Третья часть реализует обход графа в ширину +``` Python +#3 +from collections import deque + +def obhod_v_shirinu(graph, start): + visited = set() + result = [] + queue = deque([start]) + visited.add(start) + + while queue: + vershina = queue.popleft() + result.append(vershina) + + for sosed in graph.spisok_smeznosti[vershina]: + if sosed not in visited: + visited.add(sosed) + queue.append(sosed) + return result +``` +Четвертая часть реализует приложение для работы с графом +``` Python +#4 +def main(): + graph = None + + print("Программа для работы с графами") + + while True: + print("Меню:") + print("1. Инициализировать граф") + print("2. Добавить вершину") + print("3. Добавить ребро") + print("4. Удалить вершину") + print("5. Удалить ребро") + print("7. Обход в глубину") + print("6. Показать граф") + print("8. Обход в ширину") + print("0. Выход") + + choice = input("\nВыберите действие: ").strip() + + if choice == "1": + vershini_input = input("Введите вершины через пробел: ").strip() + vershini = vershini_input.split() if vershini_input else [] + graph = Graph(vershini) + print("Граф успешно инициализирован!") + + elif choice == "2": + vershina = input("Введите вершину для добавления: ").strip() + graph.add_vershina(vershina) + print(f"Вершина '{vershina}' добавлена.") + + elif choice == "3": + u = input("Введите первую вершину: ").strip() + v = input("Введите вторую вершину: ").strip() + graph.add_rebro(u, v) + print(f"Ребро между '{u}' и '{v}' добавлено.") + + elif choice == "4": + vershina = input("Введите вершину для удаления: ").strip() + graph.remove_vershina(vershina) + print(f"Вершина '{vershina}' и все связанные ребра удалены.") + + elif choice == "5": + u = input("Введите первую вершину: ").strip() + v = input("Введите вторую вершину: ").strip() + graph.remove_rebro(u, v) + print(f"Ребро между '{u}' и '{v}' удалено.") + + elif choice == "6": + print("\nТЕКУЩИЙ ГРАФ:") + graph.display() + + elif choice == "7": + start = input("Введите начальную вершину: ").strip() + result = obhod_v_glubinu(graph, start) + print(f"Результат: {result}") + + elif choice == "8": + start = input("Введите начальную вершину: ").strip() + result = obhod_v_shirinu(graph, start) + print(f"Результат: {result}") +``` +Пятая часть реализует алгоритм Дейкстры +``` Python +#5 +import heapq +from collections import deque + +def dijkstra(graph, start, end): + distances = {vershina: float('inf') for vershina in graph.vershini} + predecessors = {vershina: None for vershina in graph.vershini} + distances[start] = 0 + + priority_queue = [(0, start)] + + while priority_queue: + current_distance, current_vershina = heapq.heappop(priority_queue) + if current_vershina == end: + break + if current_distance > distances[current_vershina]: + continue + for sosed in graph.spisok_smeznosti[current_vershina]: + ves = 1 + if hasattr(graph, 'vesa_reber') and (current_vershina, sosed) in graph.vesa_reber: + ves = graph.vesa_reber[(current_vershina, sosed)] + + distance = current_distance + ves + + if distance < distances[sosed]: + distances[sosed] = distance + predecessors[sosed] = current_vershina + heapq.heappush(priority_queue, (distance, sosed)) + + if distances[end] == float('inf'): + return None, float('inf') + + path = [] + current = end + while current is not None: + path.append(current) + current = predecessors[current] + path.reverse() + + return path, distances[end] +``` +Шестая часть позволяет выполнить индивидуальное задание и получить ответ - ('5', '1', '6', '7', 11) +``` Python +#6 +graf = Graph() +graf.add_rebro('1', '5', 3) +graf.add_rebro('1', '6', 1) +graf.add_rebro('2', '5', 4) +graf.add_rebro('6', '5', 9) +graf.add_rebro('2', '3', 3) +graf.add_rebro('2', '7', 15) +graf.add_rebro('3', '6', 10) +graf.add_rebro('6', '7', 7) +graf.add_rebro('3', '7', 6) +graf.add_rebro('3', '8', 1) +graf.add_rebro('7', '4', 1) +graf.add_rebro('4', '8', 2) +print(dijkstra(graf, '5', '7')) +``` diff --git a/labs/lab_07/lab7.py b/labs/lab_07/lab7.py new file mode 100644 index 0000000..622b3e3 --- /dev/null +++ b/labs/lab_07/lab7.py @@ -0,0 +1,249 @@ +# + +#1 +class Graph: + def __init__(self, vershini=None): + if vershini: + self.vershini = vershini + else: + self.vershini = [] + self.spisok_smeznosti = {vershina: [] for vershina in self.vershini} + self.vesa_reber = {} + + def add_vershina(self, vershina): + if vershina not in self.spisok_smeznosti: + self.vershini.append(vershina) + self.spisok_smeznosti[vershina] = [] + def add_rebro(self, u, v, ves): + if u not in self.spisok_smeznosti: + self.add_vershina(u) + if v not in self.spisok_smeznosti: + self.add_vershina(v) + if v not in self.spisok_smeznosti[u]: + self.spisok_smeznosti[u].append(v) + if u not in self.spisok_smeznosti[v]: + self.spisok_smeznosti[v].append(u) + self.vesa_reber[(u, v)] = ves + self.vesa_reber[(v, u)] = ves + + def delete_vershina(self, vershina): + for sosed in self.spisok_smeznosti[vershina]: + self.spisok_smeznosti[vershina].remove(vershina) + if (vershina, sosed) in self.vesa_reber: + del self.vesa_reber[(vershina, sosed)] + if (sosed, vershina) in self.vesa_reber: + del self.vesa_reber[(sosed, vershina)] + del self.spisok_smeznosti[vershina] + self.vershini.remove(vershina) + def delete_rebro(self, u, v): + self.spisok_smeznosti[u].remove(v) + self.spisok_smeznosti[v].remove(u) + if (u, v) in self.vesa_reber: + del self.vesa_reber[(u, v)] + if (v, u) in self.vesa_reber: + del self.vesa_reber[(v, u)] + + def get_vershini(self): + return self.vershini + def get_stepen_vershini(self, vershina): + return len(self.spisok_smeznosti[vershina]) + def get_rebra(self): + rebra = [] + visited_rebra = set() + + for u in self.spisok_smeznosti: + for v in self.spisok_smeznosti[u]: + rebro = tuple(sorted((u, v))) + if rebro not in visited_rebra: + rebra.append((u, v)) + visited_rebra.add(rebro) + return rebra + def get_ves_rebra(self, u, v): + return self.vesa_reber.get((u, v), 1) + + def set_ves_rebra(self, u, v, ves): + self.vesa_reber[(u, v)] = ves + self.vesa_reber[(v, u)] = ves + + def display(self): + print("Вершины:", self.vershini) + print("Рёбра:") + rebra = self.get_rebra() + for rebro in rebra: + print(f" {rebro[0]} - {rebro[1]}") + print("\nСписок смежности:") + for vershina in sorted(self.vershini): + print(f" {vershina}: {sorted(self.spisok_smeznosti[vershina])}") + +a = Graph() +a.add_vershina('b') +print(a.display()) + + +# - + +#2 +def obhod_v_glubinu(graph, start): + visited = set() + result = [] + stack = [start] + + while stack: + vershina = stack.pop() + if vershina not in visited: + visited.add(vershina) + result.append(vershina) + for sosed in reversed(graph.spisok_smeznosti[vershina]): + if sosed not in visited: + stack.append(sosed) + return result + + +# + +#3 +from collections import deque + +def obhod_v_shirinu(graph, start): + visited = set() + result = [] + queue = deque([start]) + visited.add(start) + + while queue: + vershina = queue.popleft() + result.append(vershina) + + for sosed in graph.spisok_smeznosti[vershina]: + if sosed not in visited: + visited.add(sosed) + queue.append(sosed) + return result + + +# - + +#4 +def main(): + graph = None + + print("Программа для работы с графами") + + while True: + print("Меню:") + print("1. Инициализировать граф") + print("2. Добавить вершину") + print("3. Добавить ребро") + print("4. Удалить вершину") + print("5. Удалить ребро") + print("7. Обход в глубину") + print("6. Показать граф") + print("8. Обход в ширину") + print("0. Выход") + + choice = input("\nВыберите действие: ").strip() + + if choice == "1": + vershini_input = input("Введите вершины через пробел: ").strip() + vershini = vershini_input.split() if vershini_input else [] + graph = Graph(vershini) + print("Граф успешно инициализирован!") + + elif choice == "2": + vershina = input("Введите вершину для добавления: ").strip() + graph.add_vershina(vershina) + print(f"Вершина '{vershina}' добавлена.") + + elif choice == "3": + u = input("Введите первую вершину: ").strip() + v = input("Введите вторую вершину: ").strip() + graph.add_rebro(u, v) + print(f"Ребро между '{u}' и '{v}' добавлено.") + + elif choice == "4": + vershina = input("Введите вершину для удаления: ").strip() + graph.remove_vershina(vershina) + print(f"Вершина '{vershina}' и все связанные ребра удалены.") + + elif choice == "5": + u = input("Введите первую вершину: ").strip() + v = input("Введите вторую вершину: ").strip() + graph.remove_rebro(u, v) + print(f"Ребро между '{u}' и '{v}' удалено.") + + elif choice == "6": + print("\nТЕКУЩИЙ ГРАФ:") + graph.display() + + elif choice == "7": + start = input("Введите начальную вершину: ").strip() + result = obhod_v_glubinu(graph, start) + print(f"Результат: {result}") + + elif choice == "8": + start = input("Введите начальную вершину: ").strip() + result = obhod_v_shirinu(graph, start) + print(f"Результат: {result}") + + + +# + +#5 +import heapq +from collections import deque + +def dijkstra(graph, start, end): + distances = {vershina: float('inf') for vershina in graph.vershini} + predecessors = {vershina: None for vershina in graph.vershini} + distances[start] = 0 + + priority_queue = [(0, start)] + + while priority_queue: + current_distance, current_vershina = heapq.heappop(priority_queue) + if current_vershina == end: + break + if current_distance > distances[current_vershina]: + continue + for sosed in graph.spisok_smeznosti[current_vershina]: + ves = 1 + if hasattr(graph, 'vesa_reber') and (current_vershina, sosed) in graph.vesa_reber: + ves = graph.vesa_reber[(current_vershina, sosed)] + + distance = current_distance + ves + + if distance < distances[sosed]: + distances[sosed] = distance + predecessors[sosed] = current_vershina + heapq.heappush(priority_queue, (distance, sosed)) + + if distances[end] == float('inf'): + return None, float('inf') + + path = [] + current = end + while current is not None: + path.append(current) + current = predecessors[current] + path.reverse() + + return path, distances[end] + + +# - + +#6 +graf = Graph() +graf.add_rebro('1', '5', 3) +graf.add_rebro('1', '6', 1) +graf.add_rebro('2', '5', 4) +graf.add_rebro('6', '5', 9) +graf.add_rebro('2', '3', 3) +graf.add_rebro('2', '7', 15) +graf.add_rebro('3', '6', 10) +graf.add_rebro('6', '7', 7) +graf.add_rebro('3', '7', 6) +graf.add_rebro('3', '8', 1) +graf.add_rebro('7', '4', 1) +graf.add_rebro('4', '8', 2) +print(dijkstra(graf, '5', '7')) + + diff --git a/labs/lab_08/Lab_08_Shuranov.md b/labs/lab_08/Lab_08_Shuranov.md new file mode 100644 index 0000000..40aa0e3 --- /dev/null +++ b/labs/lab_08/Lab_08_Shuranov.md @@ -0,0 +1,424 @@ +# Лабораторная работа 8 +# Двоичные деревья поиска (Binary Search Tree) +## Цель работы +Изучение структуры данных «Двоичное дерево поиска», а также основных операций над ним. +## Задачи лабораторной работы +1. Изучение cтруктуры данных дерево +2. Реализация класса дерево +3. Применение дерева для индивидуального задания +## Словесная постановка задачи +1. Изучить как осуществляется балансировка дерева +2. Реализовать свой класс дерева и авл дерева +3. Применить этот класс для решения задачи +## Реализация структуры данных двоичное дерево +Первая часть реализует класс Node для бинарного дерева +``` Python +#1 +class Node: + def __init__(self, value): + self.value = value + self.left = None + self.right = None +``` +Вторая часть реализует класс Derevo с небалансирующемся деревом +``` Python +#2 + +class Derevo: + def __init__(self): + self.root = None + + def is_empty(self): + return self.root is None + + def insert(self, value): + if self.search(value): + print("Элемент уже существует в дереве") + return False + + new_node = Node(value) + if self.is_empty(): + self.root = new_node + return True + return self.insert_rec(self.root, new_node) + + def insert_rec(self, current, new_node): + if new_node.value < current.value: + if current.left is None: + current.left = new_node + return True + else: + return self.insert_rec(current.left, new_node) + else: + if current.right is None: + current.right = new_node + return True + else: + return self.insert_rec(current.right, new_node) + + def search(self, value): + return self.search_rec(self.root, value) + + def search_rec(self, current, value): + if current is None: + return None + if current.value == value: + return current + elif value < current.value: + return self.search_rec(current.left, value) + else: + return self.search_rec(current.right, value) + + def delete(self, value): + if self.is_empty(): + print("Дерево пустое") + return False + + if not self.search(value): + print("Элемент не найден в дереве") + return False + + self.root = self.delete_rec(self.root, value) + return True + + def delete_rec(self, current, value): + if current is None: + return None + if value < current.value: + current.left = self.delete_rec(current.left, value) + elif value > current.value: + current.right = self.delete_rec(current.right, value) + else: + if current.left is None: + return current.right + elif current.right is None: + return current.left + + min_node = self.min_find(current.right) + current.value = min_node.value + current.right = self.delete_rec(current.right, min_node.value) + + return current + + def min_find(self, node): + current = node + while current.left is not None: + current = current.left + return current + + def height(self): + return self.height_rec(self.root) + + def height_rec(self, node): + if node is None: + return 0 + left_height = self.height_rec(node.left) + right_height = self.height_rec(node.right) + return max(left_height, right_height) + 1 + + + + def obhod1(self): + result = [] + self.obhod1_rec(self.root, result) + return result + + def obhod2(self): + result = [] + self.obhod2_rec(self.root, result) + return result + + def obhod3(self): + result = [] + self.obhod1_rec(self.root, result) + return result + + + def obhod1_rec(self, node, result): + result.append(node.value) + self.obhod1_rec(node.left, result) + self.obhod1_rec(node.right, result) + + def obhod2_rec(self, node, result): + self.obhod2_rec(node.left, result) + result.append(node.value) + self.obhod2_rec(node.right, result) + + def obhod3_rec(self, node, result): + self.obhod3_rec(node.left, result) + self.obhod3_rec(node.right, result) + result.append(node.value) + + def display(self): + if self.is_empty(): + print("Дерево пустое") + return + + queue = [self.root] + level = 0 + + while queue: + level_size = len(queue) + level_values = [] + next_level = [] + + for node in queue: + if node is None: + level_values.append("None") + else: + level_values.append(node.value) + next_level.append(node.left) + next_level.append(node.right) + print(level_values) + + if any(node is not None for node in next_level): + queue = next_level + level += 1 + else: + break +``` +Третья часть тестирует данный класс +``` Python +#3 + +d = Derevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +d.display() +``` +Четвертая часть реализует класс AvlNode для бинарного дерева с балансировкой +``` Python +#4 + +class AvlNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + self.height = 1 +``` +Пятая часть реализует класс AvlDerevo с балансирующемся деревом +``` Python +#5 + +class AvlDerevo: + def __init__(self): + self.root = None + + def get_height(self, node): + if node is None: + return 0 + return node.height + + def get_balance(self, node): + if node is None: + return 0 + return self.get_height(node.left) - self.get_height(node.right) + + def update_height(self, node): + if node is not None: + node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) + + def right_rotate(self, y): + x = y.left + a = x.right + x.right = y + y.left = a + + self.update_height(y) + self.update_height(x) + return x + + def left_rotate(self, x): + y = x.right + a = y.left + y.left = x + x.right = a + + self.update_height(x) + self.update_height(y) + return y + + def is_empty(self): + return self.root is None + + def insert(self, value): + if self.search(value): + print("Элемент уже существует в дереве") + return False + + self.root = self.insert_rec(self.root, value) + return True + + def insert_rec(self, current, value): + if current is None: + return AvlNode(value) + + if value < current.value: + current.left = self.insert_rec(current.left, value) + else: + current.right = self.insert_rec(current.right, value) + + self.update_height(current) + balance = self.get_balance(current) + + if balance > 1 and value < current.left.value: + return self.right_rotate(current) + if balance < -1 and value > current.right.value: + return self.left_rotate(current) + if balance > 1 and value > current.left.value: + current.left = self.left_rotate(current.left) + return self.right_rotate(current) + if balance < -1 and value < current.right.value: + current.right = self.right_rotate(current.right) + return self.left_rotate(current) + + return current + + def search(self, value): + return self.search_rec(self.root, value) + + def search_rec(self, current, value): + if current is None: + return None + if current.value == value: + return current + elif value < current.value: + return self.search_rec(current.left, value) + else: + return self.search_rec(current.right, value) + + def delete(self, value): + if self.is_empty(): + print("Дерево пустое") + return False + + if not self.search(value): + print("Элемент не найден в дереве") + return False + + self.root = self.delete_rec(self.root, value) + return True + + def delete_rec(self, current, value): + if current is None: + return None + if value < current.value: + current.left = self.delete_rec(current.left, value) + elif value > current.value: + current.right = self.delete_rec(current.right, value) + else: + if current.left is None: + return current.right + elif current.right is None: + return current.left + + min_node = self.min_find(current.right) + current.value = min_node.value + current.right = self.delete_rec(current.right, min_node.value) + + self._update_height(current) + balance = self.get_balance(current) + + if balance > 1 and self.get_balance(current.left) >= 0: + return self.right_rotate(current) + if balance > 1 and self.get_balance(node.left) < 0: + node.left = self.left_rotate(node.left) + return self.right_rotate(node) + if balance < -1 and self.get_balance(node.right) <= 0: + return self.left_rotate(node) + if balance < -1 and self.get_balance(node.right) > 0: + node.right = self.right_rotate(node.right) + return self.left_rotate(node) + + return current + + def min_find(self, node): + current = node + while current.left is not None: + current = current.left + return current + + def height(self): + return self.height_rec(self.root) + + def height_rec(self, node): + if node is None: + return 0 + left_height = self.height_rec(node.left) + right_height = self.height_rec(node.right) + return max(left_height, right_height) + 1 + + def display(self): + if self.is_empty(): + print("Дерево пустое") + return + + queue = [self.root] + level = 0 + + while queue: + level_size = len(queue) + level_values = [] + next_level = [] + + for node in queue: + if node is None: + level_values.append("None") + else: + level_values.append(node.value) + next_level.append(node.left) + next_level.append(node.right) + print(level_values) + + if any(node is not None for node in next_level): + queue = next_level + level += 1 + else: + break + +``` +Шестая часть тестирует данный класс +``` Python +#6 + +d = AvlDerevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +d.display() +``` +Седьмая часть реализует функцию obhod для обхода дерева в ширину +``` Python +#7 + +from collections import deque + +def obhod(tree): + if tree.is_empty(): + return [] + + result = [] + queue = deque([tree.root]) + + while queue: + current_node = queue.popleft() + result.append(current_node.value) + + if current_node.left is not None: + queue.append(current_node.left) + if current_node.right is not None: + queue.append(current_node.right) + + return result + +d = Derevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +print(obhod(d)) +``` \ No newline at end of file diff --git a/labs/lab_08/derevo.py b/labs/lab_08/derevo.py new file mode 100644 index 0000000..1cf4f53 --- /dev/null +++ b/labs/lab_08/derevo.py @@ -0,0 +1,410 @@ +#1 +class Node: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + + +# + +#2 + +class Derevo: + def __init__(self): + self.root = None + + def is_empty(self): + return self.root is None + + def insert(self, value): + if self.search(value): + print("Элемент уже существует в дереве") + return False + + new_node = Node(value) + if self.is_empty(): + self.root = new_node + return True + return self.insert_rec(self.root, new_node) + + def insert_rec(self, current, new_node): + if new_node.value < current.value: + if current.left is None: + current.left = new_node + return True + else: + return self.insert_rec(current.left, new_node) + else: + if current.right is None: + current.right = new_node + return True + else: + return self.insert_rec(current.right, new_node) + + def search(self, value): + return self.search_rec(self.root, value) + + def search_rec(self, current, value): + if current is None: + return None + if current.value == value: + return current + elif value < current.value: + return self.search_rec(current.left, value) + else: + return self.search_rec(current.right, value) + + def delete(self, value): + if self.is_empty(): + print("Дерево пустое") + return False + + if not self.search(value): + print("Элемент не найден в дереве") + return False + + self.root = self.delete_rec(self.root, value) + return True + + def delete_rec(self, current, value): + if current is None: + return None + if value < current.value: + current.left = self.delete_rec(current.left, value) + elif value > current.value: + current.right = self.delete_rec(current.right, value) + else: + if current.left is None: + return current.right + elif current.right is None: + return current.left + + min_node = self.min_find(current.right) + current.value = min_node.value + current.right = self.delete_rec(current.right, min_node.value) + + return current + + def min_find(self, node): + current = node + while current.left is not None: + current = current.left + return current + + def height(self): + return self.height_rec(self.root) + + def height_rec(self, node): + if node is None: + return 0 + left_height = self.height_rec(node.left) + right_height = self.height_rec(node.right) + return max(left_height, right_height) + 1 + + + + def obhod1(self): + result = [] + self.obhod1_rec(self.root, result) + return result + + def obhod2(self): + result = [] + self.obhod2_rec(self.root, result) + return result + + def obhod3(self): + result = [] + self.obhod1_rec(self.root, result) + return result + + + def obhod1_rec(self, node, result): + result.append(node.value) + self.obhod1_rec(node.left, result) + self.obhod1_rec(node.right, result) + + def obhod2_rec(self, node, result): + self.obhod2_rec(node.left, result) + result.append(node.value) + self.obhod2_rec(node.right, result) + + def obhod3_rec(self, node, result): + self.obhod3_rec(node.left, result) + self.obhod3_rec(node.right, result) + result.append(node.value) + + def display(self): + if self.is_empty(): + print("Дерево пустое") + return + + queue = [self.root] + level = 0 + + while queue: + level_size = len(queue) + level_values = [] + next_level = [] + + for node in queue: + if node is None: + level_values.append("None") + else: + level_values.append(node.value) + next_level.append(node.left) + next_level.append(node.right) + print(level_values) + + if any(node is not None for node in next_level): + queue = next_level + level += 1 + else: + break + + +# + +#3 + +d = Derevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +d.display() + + +# + +#4 + +class AvlNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None + self.height = 1 + + +# + +#5 + +class AvlDerevo: + def __init__(self): + self.root = None + + def get_height(self, node): + if node is None: + return 0 + return node.height + + def get_balance(self, node): + if node is None: + return 0 + return self.get_height(node.left) - self.get_height(node.right) + + def update_height(self, node): + if node is not None: + node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) + + def right_rotate(self, y): + x = y.left + a = x.right + x.right = y + y.left = a + + self.update_height(y) + self.update_height(x) + return x + + def left_rotate(self, x): + y = x.right + a = y.left + y.left = x + x.right = a + + self.update_height(x) + self.update_height(y) + return y + + def is_empty(self): + return self.root is None + + def insert(self, value): + if self.search(value): + print("Элемент уже существует в дереве") + return False + + self.root = self.insert_rec(self.root, value) + return True + + def insert_rec(self, current, value): + if current is None: + return AvlNode(value) + + if value < current.value: + current.left = self.insert_rec(current.left, value) + else: + current.right = self.insert_rec(current.right, value) + + self.update_height(current) + balance = self.get_balance(current) + + if balance > 1 and value < current.left.value: + return self.right_rotate(current) + if balance < -1 and value > current.right.value: + return self.left_rotate(current) + if balance > 1 and value > current.left.value: + current.left = self.left_rotate(current.left) + return self.right_rotate(current) + if balance < -1 and value < current.right.value: + current.right = self.right_rotate(current.right) + return self.left_rotate(current) + + return current + + def search(self, value): + return self.search_rec(self.root, value) + + def search_rec(self, current, value): + if current is None: + return None + if current.value == value: + return current + elif value < current.value: + return self.search_rec(current.left, value) + else: + return self.search_rec(current.right, value) + + def delete(self, value): + if self.is_empty(): + print("Дерево пустое") + return False + + if not self.search(value): + print("Элемент не найден в дереве") + return False + + self.root = self.delete_rec(self.root, value) + return True + + def delete_rec(self, current, value): + if current is None: + return None + if value < current.value: + current.left = self.delete_rec(current.left, value) + elif value > current.value: + current.right = self.delete_rec(current.right, value) + else: + if current.left is None: + return current.right + elif current.right is None: + return current.left + + min_node = self.min_find(current.right) + current.value = min_node.value + current.right = self.delete_rec(current.right, min_node.value) + + self._update_height(current) + balance = self.get_balance(current) + + if balance > 1 and self.get_balance(current.left) >= 0: + return self.right_rotate(current) + if balance > 1 and self.get_balance(node.left) < 0: + node.left = self.left_rotate(node.left) + return self.right_rotate(node) + if balance < -1 and self.get_balance(node.right) <= 0: + return self.left_rotate(node) + if balance < -1 and self.get_balance(node.right) > 0: + node.right = self.right_rotate(node.right) + return self.left_rotate(node) + + return current + + def min_find(self, node): + current = node + while current.left is not None: + current = current.left + return current + + def height(self): + return self.height_rec(self.root) + + def height_rec(self, node): + if node is None: + return 0 + left_height = self.height_rec(node.left) + right_height = self.height_rec(node.right) + return max(left_height, right_height) + 1 + + def display(self): + if self.is_empty(): + print("Дерево пустое") + return + + queue = [self.root] + level = 0 + + while queue: + level_size = len(queue) + level_values = [] + next_level = [] + + for node in queue: + if node is None: + level_values.append("None") + else: + level_values.append(node.value) + next_level.append(node.left) + next_level.append(node.right) + print(level_values) + + if any(node is not None for node in next_level): + queue = next_level + level += 1 + else: + break + + + +# + +#6 + +d = AvlDerevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +d.display() + +# + +#7 + +from collections import deque + +def obhod(tree): + if tree.is_empty(): + return [] + + result = [] + queue = deque([tree.root]) + + while queue: + current_node = queue.popleft() + result.append(current_node.value) + + if current_node.left is not None: + queue.append(current_node.left) + if current_node.right is not None: + queue.append(current_node.right) + + return result + +d = Derevo() +d.insert(5) +d.insert(1) +d.insert(2) +d.insert(6) +print(obhod(d)) +# - + + diff --git a/labs/lab_08/lab_08.py b/labs/lab_08/lab_08.py new file mode 100644 index 0000000..a5c954a --- /dev/null +++ b/labs/lab_08/lab_08.py @@ -0,0 +1,320 @@ +# + +#1 + +import math + +class VelikiyCipher: + def __init__(self, key1, key2): + self.key1 = self.validate_key(key1) + self.key2 = self.validate_key(key2) + + def validate_key(self, key): + key = [int(k) for k in key] + + return key + + def create_permutation_order(self, key): + indexed_key = list(enumerate(key)) + sorted_key = sorted(indexed_key, key=lambda x: x[1]) + return [i for i, _ in sorted_key] + + def apply_transposition(self, text, key, encrypt=True): + key_order = self.create_permutation_order(key) + n_cols = len(key) + n_rows = math.ceil(len(text) / n_cols) + + empty_cells = n_rows * n_cols - len(text) + + matrix = [['' for _ in range(n_cols)] for _ in range(n_rows)] + + if encrypt: + pos = 0 + for row in range(n_rows): + for col in range(n_cols): + if pos < len(text): + matrix[row][col] = text[pos] + pos += 1 + else: + matrix[row][col] = '' + + result = [] + for col_idx in key_order: + for row in range(n_rows): + if matrix[row][col_idx]: + result.append(matrix[row][col_idx]) + + else: + chars_per_col = [0] * n_cols + full_cols = len(text) % n_cols + if full_cols == 0: + full_cols = n_cols + + for col_idx in key_order: + if col_idx < full_cols: + chars_per_col[col_idx] = n_rows + else: + chars_per_col[col_idx] = n_rows - 1 + + pos = 0 + for col_idx in key_order: + for row in range(chars_per_col[col_idx]): + if pos < len(text): + matrix[row][col_idx] = text[pos] + pos += 1 + result = [] + for row in range(n_rows): + for col in range(n_cols): + if matrix[row][col]: + result.append(matrix[row][col]) + + return ''.join(result) + + def encrypt(self, plaintext): + text = ''.join(plaintext.upper().split()) + + step1 = self.apply_transposition(text, self.key1, encrypt=True) + ciphertext = self.apply_transposition(step1, self.key2, encrypt=True) + + return ciphertext + + def decrypt(self, ciphertext): + text = ciphertext.upper() + + step1 = self.apply_transposition(text, self.key2, encrypt=False) + plaintext = self.apply_transposition(step1, self.key1, encrypt=False) + + return plaintext + +def input_key(key_number): + while True: + try: + key_str = input(f"Введите ключ {key_number}: ").strip() + + if not key_str: + print("Ключ не может быть пустым") + continue + + key_list = [int(x) for x in key_str.split()] + + if len(key_list) < 2: + print("Ключ должен содержать минимум 2 цифры") + continue + + return key_list + + except ValueError: + print("Ошибка: вводите только цифры, разделенные пробелами") + except Exception as e: + print(f"Ошибка: {e}") + +def input_operation(): + while True: + print("\nВыберите операцию:") + print("1 - Зашифровать текст") + print("2 - Расшифровать текст") + print("3 - Выход") + + choice = input("Ваш выбор: ").strip() + + if choice in ['1', '2', '3']: + return choice + else: + print("Неверный выбор. Попробуйте снова.") + +def run_cipher_program(): + print("\nВведите ключи:") + key1 = input_key(1) + key2 = input_key(2) + + try: + cipher = VelikiyCipher(key1, key2) + print(f"\nКлюч 1: {key1}") + print(f"Ключ 2: {key2}") + print("Шифр успешно инициализирован!") + except ValueError as e: + print(f"\nОшибка инициализации шифра: {e}") + return + + while True: + operation = input_operation() + + if operation == '1': + plaintext = input("\nВведите текст для шифрования: ").strip() + if plaintext: + try: + encrypted = cipher.encrypt(plaintext) + print(f"\nИсходный текст: {plaintext}") + print(f"Зашифрованный текст: {encrypted}") + except Exception as e: + print(f"Ошибка при шифровании: {e}") + else: + print("Текст не может быть пустым") + + elif operation == '2': + ciphertext = input("\nВведите текст для дешифрования: ").strip() + if ciphertext: + try: + decrypted = cipher.decrypt(ciphertext) + print(f"\nЗашифрованный текст: {ciphertext}") + print(f"Расшифрованный текст: {decrypted}") + except Exception as e: + print(f"Ошибка при дешифровании: {e}") + else: + print("Текст не может быть пустым") + + elif operation == '3': + print("\nВыход из программы.") + break + +if __name__ == "__main__": + try: + run_cipher_program() + except KeyboardInterrupt: + print("\n\nПрограмма прервана пользователем.") + except Exception as e: + print(f"\nПроизошла непредвиденная ошибка: {e}") + +# + +#2 + +import random +import hashlib +import hmac +import os + + +import matplotlib.pyplot as plt +plt.style.use('seaborn-v0_8-whitegrid') + +P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +A = 0 +B = 7 +Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 +Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 +N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +class Point: + def __init__(self, x, y, infinity=False): + self.x = x + self.y = y + self.infinity = infinity + + def __eq__(self, other): + if self.infinity and other.infinity: return True + if self.infinity or other.infinity: return False + return self.x == other.x and self.y == other.y + + def __repr__(self): + if self.infinity: return "Point(Infinity)" + return f"Point({hex(self.x)}, {hex(self.y)})" + +def mod_inverse(a, m): + return pow(a, m - 2, m) + +def point_add(p1, p2): + if p1.infinity: return p2 + if p2.infinity: return p1 + + if p1.x == p2.x and p1.y != p2.y: + return Point(0, 0, True) + + if p1.x == p2.x and p1.y == p2.y: + + if p1.y == 0: return Point(0, 0, True) + lam = (3 * p1.x**2 + A) * mod_inverse(2 * p1.y, P) + else: + + lam = (p2.y - p1.y) * mod_inverse(p2.x - p1.x, P) + + lam %= P + x3 = (lam**2 - p1.x - p2.x) % P + y3 = (lam * (p1.x - x3) - p1.y) % P + return Point(x3, y3) + +def scalar_mult(k, point): + result = Point(0, 0, True) + addend = point + while k: + if k & 1: + result = point_add(result, addend) + addend = point_add(addend, addend) + k >>= 1 + return result + +G = Point(Gx, Gy) + +class ECIES: + def __init__(self): + pass + + def generate_keys(self): + priv = random.randint(1, N - 1) + pub = scalar_mult(priv, G) + return priv, pub + + def kdf(self, secret_point): + x_bytes = secret_point.x.to_bytes(32, 'big') + hash_bytes = hashlib.sha512(x_bytes).digest() + return hash_bytes[:32], hash_bytes[32:] + + def encrypt_symmetric(self, key, plaintext): + ciphertext = bytearray() + for i, b in enumerate(plaintext): + ciphertext.append(b ^ key[i % len(key)]) + return bytes(ciphertext) + + def decrypt_symmetric(self, key, ciphertext): + return self.encrypt_symmetric(key, ciphertext) + + def encrypt(self, pub_key, message): + k = random.randint(1, N - 1) + R = scalar_mult(k, G) + + S = scalar_mult(k, pub_key) + if S.infinity: + raise ValueError("Invalid shared secret") + + Ke, Km = self.kdf(S) + + if isinstance(message, str): + message = message.encode('utf-8') + C = self.encrypt_symmetric(Ke, message) + + tag = hmac.new(Km, C, hashlib.sha256).digest() + + return R, C, tag + + def decrypt(self, priv_key, R, C, tag): + S = scalar_mult(priv_key, R) + if S.infinity: + raise ValueError("Invalid shared secret") + + Ke, Km = self.kdf(S) + + calc_tag = hmac.new(Km, C, hashlib.sha256).digest() + if not hmac.compare_digest(tag, calc_tag): + raise ValueError("MAC verification failed! Message corrupted.") + + plaintext = self.decrypt_symmetric(Ke, C) + return plaintext.decode('utf-8') + +ecies = ECIES() + +alice_priv, alice_pub = ecies.generate_keys() +print(f"Alice Private: {hex(alice_priv)}") +print(f"Alice Public: {alice_pub}") + +msg = "Hello, Elliptic Curve World!" +print(f"\nOriginal Message: {msg}") + +R, C, tag = ecies.encrypt(alice_pub, msg) +print(f"Encrypted (R): {R}") +print(f"Encrypted (C hex): {C.hex()}") +print(f"Tag (hex): {tag.hex()}") + + +decrypted = ecies.decrypt(alice_priv, R, C, tag) +print(f"\nDecrypted: {decrypted}") +# - + + diff --git a/labs/lab_09/Lab_09_Shuranov.md b/labs/lab_09/Lab_09_Shuranov.md new file mode 100644 index 0000000..ff24da7 --- /dev/null +++ b/labs/lab_09/Lab_09_Shuranov.md @@ -0,0 +1,333 @@ +# Лабораторная работа 9 +# Криптоалгоритмы +## Цель работы +Получение практических навыков защиты программного обеспечения от несанкционированного доступа путем шифрования с использованием криптоалгоритмов. +## Задачи лабораторной работы +1. Изучение различных алгоритмов шифрования +2. Реализация двух индивидуальных алгоритмов +3. Применение алгоритмов для шифровки и дешифровки сообщений +## Словесная постановка задачи +1. Изучить какие существуют виды шифров +2. Реализовать два вида шифрования +3. Применить это шифрования для сообщения +## Реализация алгоритмов шифрования +Первая часть реализует класс для Великого шифра Россиньоля +``` Python +#1 + +import math + +class VelikiyCipher: + def __init__(self, key1, key2): + self.key1 = self.validate_key(key1) + self.key2 = self.validate_key(key2) + + def validate_key(self, key): + key = [int(k) for k in key] + + return key + + def create_permutation_order(self, key): + indexed_key = list(enumerate(key)) + sorted_key = sorted(indexed_key, key=lambda x: x[1]) + return [i for i, _ in sorted_key] + + def apply_transposition(self, text, key, encrypt=True): + key_order = self.create_permutation_order(key) + n_cols = len(key) + n_rows = math.ceil(len(text) / n_cols) + + empty_cells = n_rows * n_cols - len(text) + + matrix = [['' for _ in range(n_cols)] for _ in range(n_rows)] + + if encrypt: + pos = 0 + for row in range(n_rows): + for col in range(n_cols): + if pos < len(text): + matrix[row][col] = text[pos] + pos += 1 + else: + matrix[row][col] = '' + + result = [] + for col_idx in key_order: + for row in range(n_rows): + if matrix[row][col_idx]: + result.append(matrix[row][col_idx]) + + else: + chars_per_col = [0] * n_cols + full_cols = len(text) % n_cols + if full_cols == 0: + full_cols = n_cols + + for col_idx in key_order: + if col_idx < full_cols: + chars_per_col[col_idx] = n_rows + else: + chars_per_col[col_idx] = n_rows - 1 + + pos = 0 + for col_idx in key_order: + for row in range(chars_per_col[col_idx]): + if pos < len(text): + matrix[row][col_idx] = text[pos] + pos += 1 + result = [] + for row in range(n_rows): + for col in range(n_cols): + if matrix[row][col]: + result.append(matrix[row][col]) + + return ''.join(result) + + def encrypt(self, plaintext): + text = ''.join(plaintext.upper().split()) + + step1 = self.apply_transposition(text, self.key1, encrypt=True) + ciphertext = self.apply_transposition(step1, self.key2, encrypt=True) + + return ciphertext + + def decrypt(self, ciphertext): + text = ciphertext.upper() + + step1 = self.apply_transposition(text, self.key2, encrypt=False) + plaintext = self.apply_transposition(step1, self.key1, encrypt=False) + + return plaintext + +def input_key(key_number): + while True: + try: + key_str = input(f"Введите ключ {key_number}: ").strip() + + if not key_str: + print("Ключ не может быть пустым") + continue + + key_list = [int(x) for x in key_str.split()] + + if len(key_list) < 2: + print("Ключ должен содержать минимум 2 цифры") + continue + + return key_list + + except ValueError: + print("Ошибка: вводите только цифры, разделенные пробелами") + except Exception as e: + print(f"Ошибка: {e}") + +def input_operation(): + while True: + print("\nВыберите операцию:") + print("1 - Зашифровать текст") + print("2 - Расшифровать текст") + print("3 - Выход") + + choice = input("Ваш выбор: ").strip() + + if choice in ['1', '2', '3']: + return choice + else: + print("Неверный выбор. Попробуйте снова.") + +def run_cipher_program(): + print("\nВведите ключи:") + key1 = input_key(1) + key2 = input_key(2) + + try: + cipher = VelikiyCipher(key1, key2) + print(f"\nКлюч 1: {key1}") + print(f"Ключ 2: {key2}") + print("Шифр успешно инициализирован!") + except ValueError as e: + print(f"\nОшибка инициализации шифра: {e}") + return + + while True: + operation = input_operation() + + if operation == '1': + plaintext = input("\nВведите текст для шифрования: ").strip() + if plaintext: + try: + encrypted = cipher.encrypt(plaintext) + print(f"\nИсходный текст: {plaintext}") + print(f"Зашифрованный текст: {encrypted}") + except Exception as e: + print(f"Ошибка при шифровании: {e}") + else: + print("Текст не может быть пустым") + + elif operation == '2': + ciphertext = input("\nВведите текст для дешифрования: ").strip() + if ciphertext: + try: + decrypted = cipher.decrypt(ciphertext) + print(f"\nЗашифрованный текст: {ciphertext}") + print(f"Расшифрованный текст: {decrypted}") + except Exception as e: + print(f"Ошибка при дешифровании: {e}") + else: + print("Текст не может быть пустым") + + elif operation == '3': + print("\nВыход из программы.") + break + +if __name__ == "__main__": + try: + run_cipher_program() + except KeyboardInterrupt: + print("\n\nПрограмма прервана пользователем.") + except Exception as e: + print(f"\nПроизошла непредвиденная ошибка: {e}") +``` +Вторая часть реализует класс для алгоритма ECIES +``` Python +#2 + +import random +import hashlib +import hmac +import os + + +import matplotlib.pyplot as plt +plt.style.use('seaborn-v0_8-whitegrid') + +P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +A = 0 +B = 7 +Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 +Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 +N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +class Point: + def __init__(self, x, y, infinity=False): + self.x = x + self.y = y + self.infinity = infinity + + def __eq__(self, other): + if self.infinity and other.infinity: return True + if self.infinity or other.infinity: return False + return self.x == other.x and self.y == other.y + + def __repr__(self): + if self.infinity: return "Point(Infinity)" + return f"Point({hex(self.x)}, {hex(self.y)})" + +def mod_inverse(a, m): + return pow(a, m - 2, m) + +def point_add(p1, p2): + if p1.infinity: return p2 + if p2.infinity: return p1 + + if p1.x == p2.x and p1.y != p2.y: + return Point(0, 0, True) + + if p1.x == p2.x and p1.y == p2.y: + + if p1.y == 0: return Point(0, 0, True) + lam = (3 * p1.x**2 + A) * mod_inverse(2 * p1.y, P) + else: + + lam = (p2.y - p1.y) * mod_inverse(p2.x - p1.x, P) + + lam %= P + x3 = (lam**2 - p1.x - p2.x) % P + y3 = (lam * (p1.x - x3) - p1.y) % P + return Point(x3, y3) + +def scalar_mult(k, point): + result = Point(0, 0, True) + addend = point + while k: + if k & 1: + result = point_add(result, addend) + addend = point_add(addend, addend) + k >>= 1 + return result + +G = Point(Gx, Gy) + +class ECIES: + def __init__(self): + pass + + def generate_keys(self): + priv = random.randint(1, N - 1) + pub = scalar_mult(priv, G) + return priv, pub + + def kdf(self, secret_point): + x_bytes = secret_point.x.to_bytes(32, 'big') + hash_bytes = hashlib.sha512(x_bytes).digest() + return hash_bytes[:32], hash_bytes[32:] + + def encrypt_symmetric(self, key, plaintext): + ciphertext = bytearray() + for i, b in enumerate(plaintext): + ciphertext.append(b ^ key[i % len(key)]) + return bytes(ciphertext) + + def decrypt_symmetric(self, key, ciphertext): + return self.encrypt_symmetric(key, ciphertext) + + def encrypt(self, pub_key, message): + k = random.randint(1, N - 1) + R = scalar_mult(k, G) + + S = scalar_mult(k, pub_key) + if S.infinity: + raise ValueError("Invalid shared secret") + + Ke, Km = self.kdf(S) + + if isinstance(message, str): + message = message.encode('utf-8') + C = self.encrypt_symmetric(Ke, message) + + tag = hmac.new(Km, C, hashlib.sha256).digest() + + return R, C, tag + + def decrypt(self, priv_key, R, C, tag): + S = scalar_mult(priv_key, R) + if S.infinity: + raise ValueError("Invalid shared secret") + + Ke, Km = self.kdf(S) + + calc_tag = hmac.new(Km, C, hashlib.sha256).digest() + if not hmac.compare_digest(tag, calc_tag): + raise ValueError("MAC verification failed! Message corrupted.") + + plaintext = self.decrypt_symmetric(Ke, C) + return plaintext.decode('utf-8') + +ecies = ECIES() + +alice_priv, alice_pub = ecies.generate_keys() +print(f"Alice Private: {hex(alice_priv)}") +print(f"Alice Public: {alice_pub}") + +msg = "Hello, Elliptic Curve World!" +print(f"\nOriginal Message: {msg}") + +R, C, tag = ecies.encrypt(alice_pub, msg) +print(f"Encrypted (R): {R}") +print(f"Encrypted (C hex): {C.hex()}") +print(f"Tag (hex): {tag.hex()}") + + +decrypted = ecies.decrypt(alice_priv, R, C, tag) +print(f"\nDecrypted: {decrypted}") +``` \ No newline at end of file diff --git a/labs/lab_10/10.py b/labs/lab_10/10.py new file mode 100644 index 0000000..16d15df --- /dev/null +++ b/labs/lab_10/10.py @@ -0,0 +1,75 @@ +# + +def lcs_length(s1, s2): + n, m = len(s1), len(s2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + for j in range(1, m + 1): + if s1[i-1] == s2[j-1]: + dp[i][j] = dp[i-1][j-1] + 1 + else: + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + return dp[n][m] + +print("LCS length:", lcs_length("abcde", "ace")) +def lis_length(arr): + if not arr: + return 0 + n = len(arr) + dp = [1] * n + for i in range(1, n): + for j in range(i): + if arr[j] < arr[i]: + dp[i] = max(dp[i], dp[j] + 1) + return max(dp) + +print("LIS length:", lis_length([10, 9, 2, 5, 3, 7, 101, 18])) +def min_coins(coins, amount): + dp = [float('inf')] * (amount + 1) + dp[0] = 0 + for c in coins: + for x in range(c, amount + 1): + if dp[x - c] + 1 < dp[x]: + dp[x] = dp[x - c] + 1 + return dp[amount] if dp[amount] != float('inf') else -1 + +print("Min coins for 11:", min_coins([1, 3, 4], 11)) +def levenshtein_dist(s1, s2): + n, m = len(s1), len(s2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + dp[i][0] = i + for j in range(m + 1): + dp[0][j] = j + + for i in range(1, n + 1): + for j in range(1, m + 1): + if s1[i-1] == s2[j-1]: + dp[i][j] = dp[i-1][j-1] + else: + dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + return dp[n][m] + +print("Levenshtein:", levenshtein_dist("kitten", "sitting")) +def matrix_chain_min_mult(dims): + n = len(dims) - 1 + if n <= 1: + return 0 + dp = [[0] * n for _ in range(n)] + + for length in range(2, n + 1): + for i in range(n - length + 1): + j = i + length - 1 + dp[i][j] = float('inf') + for k in range(i, j): + cost = dp[i][k] + dp[k+1][j] + dims[i] * dims[k+1] * dims[j+1] + if cost < dp[i][j]: + dp[i][j] = cost + return dp[0][n-1] + +print("Min mult:", matrix_chain_min_mult([10, 30, 5, 60])) + +# - + + diff --git a/labs/lab_10/Lab_10_Shuranov.md b/labs/lab_10/Lab_10_Shuranov.md new file mode 100644 index 0000000..5752901 --- /dev/null +++ b/labs/lab_10/Lab_10_Shuranov.md @@ -0,0 +1,72 @@ +```Python +def lcs_length(s1, s2): + n, m = len(s1), len(s2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + for j in range(1, m + 1): + if s1[i-1] == s2[j-1]: + dp[i][j] = dp[i-1][j-1] + 1 + else: + dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + return dp[n][m] + +print("LCS length:", lcs_length("abcde", "ace")) +def lis_length(arr): + if not arr: + return 0 + n = len(arr) + dp = [1] * n + for i in range(1, n): + for j in range(i): + if arr[j] < arr[i]: + dp[i] = max(dp[i], dp[j] + 1) + return max(dp) + +print("LIS length:", lis_length([10, 9, 2, 5, 3, 7, 101, 18])) +def min_coins(coins, amount): + dp = [float('inf')] * (amount + 1) + dp[0] = 0 + for c in coins: + for x in range(c, amount + 1): + if dp[x - c] + 1 < dp[x]: + dp[x] = dp[x - c] + 1 + return dp[amount] if dp[amount] != float('inf') else -1 + +print("Min coins for 11:", min_coins([1, 3, 4], 11)) +def levenshtein_dist(s1, s2): + n, m = len(s1), len(s2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + dp[i][0] = i + for j in range(m + 1): + dp[0][j] = j + + for i in range(1, n + 1): + for j in range(1, m + 1): + if s1[i-1] == s2[j-1]: + dp[i][j] = dp[i-1][j-1] + else: + dp[i][j] = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + return dp[n][m] + +print("Levenshtein:", levenshtein_dist("kitten", "sitting")) +def matrix_chain_min_mult(dims): + n = len(dims) - 1 + if n <= 1: + return 0 + dp = [[0] * n for _ in range(n)] + + for length in range(2, n + 1): + for i in range(n - length + 1): + j = i + length - 1 + dp[i][j] = float('inf') + for k in range(i, j): + cost = dp[i][k] + dp[k+1][j] + dims[i] * dims[k+1] * dims[j+1] + if cost < dp[i][j]: + dp[i][j] = cost + return dp[0][n-1] + +print("Min mult:", matrix_chain_min_mult([10, 30, 5, 60])) +``` \ No newline at end of file