- •1. Генерация исходных данных
- •2. Быстрая сортировка (Quick Sort)
- •3. Бинарный поиск
- •4. Проверка результатов
- •1. Генерация тестовых данных
- •2. Реализация сортировки пузырьком
- •3. Реализация быстрой сортировки
- •4. Функция сравнения алгоритмов
- •5. Визуализация результатов
- •6. Вывод результатов в таблице
- •1. Сортировка и поиск (Задание 1.1)
- •2. Сравнение алгоритмов (Задание 1.2)
Министерство цифрового развития, связи и массовых коммуникаций Российской Федерации
Ордена Трудового Красного Знамени федеральное государственное бюджетное образовательное учреждение высшего образования
МОСКОВСКИЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
СВЯЗИ И ИНФОРМАТИКИ
(МТУСИ)
Факультет "Сети и системные связи"
Кафедра “ВвИТ"
ОТЧЕТ
по дисциплине "Введение в информационные технологии"
на тему: Введение в численные методы на Python: Алгоритмы сортировки и поиска
"Лабораторная работа №4"
Выполнил
Студент гр. БИН2412 ____________________ Джумъаев Ф.Н.
Проверил
Преподаватель ___________________
Дата защиты _________2025г.
Москва 2025
Цель работы
Знакомство с численными методами для решения задач:
- сортировка элементов массива
- поиск элементов в упорядоченном массиве
Задание 1.1 (основная часть) “Реализация алгоритма сортировки и поиска элемента в массиве”
Задание 1.2 (дополнительная часть) “Сравнение эффективности алгоритмов сортировки”
Введение
Блок 1 “Алгоритмы сортировки и поиска” Рассмотрим задачу по поиску элемента в неупорядоченном наборе чисел (массиве). Решение будем производить в 2 этапа: упорядочим (т.е. расположим по порядку – по возрастанию) элементы массива, а затем найдем нужное в упорядоченном наборе. Рассмотрим два популярных алгоритма сортировки: осортировку пузырьком и быструю сортировку. Сортировка пузырьком (Bubble Sort) — это простой алгоритм сортировки, который работает путем многократного прохода через список и сравнения соседних элементов. Если порядок неправильный, элементы меняются местами. Этот процесс продолжается до тех пор, пока массив не будет отсортирован. Алгоритм: 1. Начинаем с первого элемента и сравниваем его со следующим элементом. 2. Если первый элемент больше второго, меняем их местами. 3. Продолжаем сравнивать следующий элемент с последующим и так далее до конца списка. 4. После одного полного прохода самый большой элемент окажется на своем месте (в конце списка). 5. Повторяем процесс для оставшихся элементов, исключая уже отсортированные. 6. Процесс повторяется до тех пор, пока весь массив не станет отсортированным. Преимуществом метода является простота реализации, в то время как существенным недостатком является медленная работа на больших наборах данных из-за квадратичной сложности алгоритма (предлагается самостоятельно изучить сложности алгоритмов и оценить сортировку пузырьком как один из методов обработки массива данных). Быстрая сортировка (QuickSort) — это эффективный рекурсивный алгоритм сортировки, основанный на принципе "разделяй и властвуй”. Алгоритм выбирает один элемент (опорный) и разбивает массив на две части: элементы меньше опорного и элементы больше опорного. Затем этот процесс применяется рекурсивно к обеим частям. Алгоритм: 1. Выбираем опорный элемент (обычно последний элемент). 2. Разделяем массив на две части: элементы меньшие опорного и большие опорного. 3. Рекурсивно применяем быструю сортировку к каждой из частей. 4. Когда размеры подмассивов становятся равными единице, массив считается отсортированным. Преимуществом метода является высокая эффективность на практике благодаря своей средней сложности O(n*log(n)). К недостаткам можно отнести требование дополнительной памяти для стека рекурсии. Сравнивая эти методы между собой, можно отметить, что хотя сортировка пузырьком проще в реализации, она менее эффективна на больших объемах данных. Быстрая сортировка же является одним из наиболее эффективных методов сортировки и широко используется на практике. Теперь вернемся к исходной задаче по поиску элемента в неупорядоченном массиве. После того, как мы упорядочили массив, можно воспользоваться бинарным поиском для нахождения искомого элемента. По своей логической структуре этот метод похож на дихотомию, которую мы изучали в разделе поиска корней уравнения на отрезке. Он работает по принципу деления массива пополам на каждом шаге, что позволяет значительно сократить количество проверок: каждую итерацию мы сравниваем средний элемент диапазона с искомым и берем либо большую (в случае, если средний элемент меньше искомого), либо меньшую (если средний элемент больше искомого) половину для следующей итерации.
Задания 1.1
import random # Генерация исходного массива arr = [] minNum = 0.0 maxNum = 100.0 N = 10000 for _ in range(N): arr.append(random.uniform(minNum, maxNum)) arrCopy = arr.copy() # Создаем копию для проверки x = arr[random.randint(0, N - 1)] # Элемент, который будем искать # -------------------------------------------------- # Задача 1: Сортировка массива (быстрая сортировка) # -------------------------------------------------- def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) arrSort = quick_sort(arr) # -------------------------------------------------- # Задача 2: Бинарный поиск в отсортированном массиве # -------------------------------------------------- def binary_search(sorted_arr, target): low = 0 high = len(sorted_arr) - 1 while low <= high: mid = (low + high) // 2 guess = sorted_arr[mid] if guess == target: return mid if guess > target: high = mid - 1 else: low = mid + 1 return -1 # В нашем случае элемент гарантированно есть idx = binary_search(arrSort, x) # -------------------------------------------------- # Проверка (не изменять!) # -------------------------------------------------- # Алгоритм сортировки вставками для проверки def insSort(nums): for i in range(1, len(nums)): item_to_insert = nums[i] j = i - 1 while j >= 0 and nums[j] > item_to_insert: nums[j + 1] = nums[j] j -= 1 nums[j + 1] = item_to_insert insSort(arrCopy) if arrSort == arrCopy and arrSort[idx] == x: print("Good") else: print("Bad")
1. Генерация исходных данных
arr = []
minNum = 0.0
maxNum = 100.0
N = 10000
for _ in range(N):
arr.append(random.uniform(minNum, maxNum))
arrCopy = arr.copy()
x = arr[random.randint(0, N - 1)]
Создается массив arr из 10000 случайных чисел в диапазоне [0.0, 100.0]
arrCopy - точная копия исходного массива (нужна для последующей проверки)
x - случайно выбранный элемент из массива, который мы будем искать
2. Быстрая сортировка (Quick Sort)
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
arrSort = quick_sort(arr)
Алгоритм работает по принципу "разделяй и властвуй":
Выбирается опорный элемент (pivot)
Массив делится на три части:
left - элементы меньше pivot
middle - элементы равные pivot
right - элементы больше pivot
Рекурсивно сортируются части left и right
Результат - объединение отсортированных частей
Сложность: O(n log n) в среднем случае, O(n²) в худшем (редко)
3. Бинарный поиск
def binary_search(sorted_arr, target):
low = 0
high = len(sorted_arr) - 1
while low <= high:
mid = (low + high) // 2
guess = sorted_arr[mid]
if guess == target:
return mid
if guess > target:
high = mid - 1
else:
low = mid + 1
return -1
idx = binary_search(arrSort, x)
Алгоритм требует предварительно отсортированного массива:
Устанавливаются границы поиска (low и high)
Находится средний элемент (mid)
Если элемент найден - возвращается его индекс
Если искомый элемент меньше - поиск продолжается в левой части
Если больше - в правой части
Сложность: O(log n)
4. Проверка результатов
def insSort(nums):
for i in range(1, len(nums)):
item_to_insert = nums[i]
j = i - 1
while j >= 0 and nums[j] > item_to_insert:
nums[j + 1] = nums[j]
j -= 1
nums[j + 1] = item_to_insert
insSort(arrCopy)
if arrSort == arrCopy and arrSort[idx] == x:
print("Good")
else:
print("Bad")
Для проверки используется сортировка вставками (insertion sort)
Сравниваются:
Отсортированный массив arrSort с эталонным arrCopy
Найденный элемент arrSort[idx] с искомым значением x
Если все условия выполнены - выводится "Good", иначе "Bad"
Ключевые особенности кода:
Использование рекурсии в быстрой сортировке
Работа с вещественными числами (float)
Создание копии массива для проверки
Гарантированное наличие искомого элемента в массиве
Демонстрация двух принципиально разных алгоритмов сортировки
Программа наглядно демонстрирует:
Эффективность быстрой сортировки для больших массивов
Важность предварительной сортировки для бинарного поиска
Способы проверки корректности работы алгоритмов
Задания 1.2
import random import time import matplotlib.pyplot as plt # Генерация исходного массива def generate_array(size): minNum = 0.0 maxNum = 100.0 return [random.uniform(minNum, maxNum) for _ in range(size)] # Сортировка пузырьком с подсчетом операций def bubble_sort(arr): operations = 0 n = len(arr) for i in range(n): swapped = False for j in range(0, n - i - 1): operations += 1 # Счетчик сравнений if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] operations += 3 # Счетчик перестановок (3 операции) swapped = True if not swapped: break return operations # Быстрая сортировка с подсчетом операций def quick_sort(arr, operations=[0]): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] operations[0] += len(arr) # Счетчик операций сравнения operations[0] += len(left) + len(right) # Счетчик операций перемещения return quick_sort(left, operations) + middle + quick_sort(right, operations) # Функция для сравнения алгоритмов def compare_sorting_algorithms(sizes): bubble_times = [] quick_times = [] bubble_ops = [] quick_ops = [] for size in sizes: arr = generate_array(size) # Тестируем сортировку пузырьком arr_copy = arr.copy() start_time = time.time() ops = bubble_sort(arr_copy) bubble_times.append(time.time() - start_time) bubble_ops.append(ops) # Тестируем быструю сортировку arr_copy = arr.copy() ops_counter = [0] start_time = time.time() quick_sort(arr_copy, ops_counter) quick_times.append(time.time() - start_time) quick_ops.append(ops_counter[0]) # Визуализация результатов plt.figure(figsize=(12, 5)) # График времени выполнения plt.subplot(1, 2, 1) plt.plot(sizes, bubble_times, label='Сортировка пузырьком') plt.plot(sizes, quick_times, label='Быстрая сортировка') plt.xlabel('Размер массива') plt.ylabel('Время (секунды)') plt.title('Сравнение времени выполнения') plt.legend() plt.grid() # График количества операций plt.subplot(1, 2, 2) plt.plot(sizes, bubble_ops, label='Сортировка пузырьком') plt.plot(sizes, quick_ops, label='Быстрая сортировка') plt.xlabel('Размер массива') plt.ylabel('Количество операций') plt.title('Сравнение количества операций') plt.legend() plt.grid() plt.tight_layout() plt.show() return bubble_times, quick_times, bubble_ops, quick_ops # Размеры массивов для тестирования array_sizes = [100, 500, 1000, 2000, 5000] # Запуск сравнения bubble_t, quick_t, bubble_o, quick_o = compare_sorting_algorithms(array_sizes) # Вывод результатов в табличном виде print("\nСравнение алгоритмов сортировки:") print(f"{'Размер':<10}{'Пузырьком (сек)':<20}{'Быстрая (сек)':<20}{'Пузырьком (оп)':<20}{'Быстрая (оп)':<20}") for i, size in enumerate(array_sizes): print(f"{size:<10}{bubble_t[i]:<20.5f}{quick_t[i]:<20.5f}{bubble_o[i]:<20}{quick_o[i]:<20}")
