Добавил:
coffee4ek
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Отчёты Ерофеев / Лабы / Отчёт 2
.pdf
ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ
ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ
УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ
«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»
(СПбГУТ)
Факультет информационных технологий и программной инженерии (ИТПИ)
Кафедра программной инженерии и вычислительной техники (ПИиВТ)
Дисциплина: «Алгоритмические основы программной инженерии»
Лабораторная работа №2.
Выполнил: Студент группы ИКПИ-41
Летягина Е. А.
Подпись
Принял: Доцент кафедры ПИиВТ
Ерофеев Сергей Анатольевич
Подпись
«_____»________ 2025
г. Санкт-Петербург
2025 г.

1. Постановка задачи
массива
Исходный
- -
[8, 3, 5, 1, 9, 2]
1 3 Вставка на 0
[3, 8, 5, 1, 9, 2]
2 5 Вставка на
1
[3, 5, 8, 1, 9, 2]
3 1
Вставка на 0
[1, 3, 5, 8, 9, 2]
месте
5 2 Вставка на 1
[1, 2, 3, 5, 8, 9]
Разработать программу на языке Си для сортировки чисел из файла
методом бинарных вставок. Пользователь может выбрать использование
статического или динамического массива для хранения данных. Программа
должна считывать числа из файла, сортировать их и выводить результат.
Также необходимо посчитать сложность алгоритма.
2. Техническое задание
1. Маршрут подпрограммы сортировки массива методом
бинарных вставок
1) Программа получает массив чисел (статический или
динамический), который необходимо отсортировать.
2) Для каждого элемента массива, начиная со второго (i = 1 до n-
1):
a) Инициализация переменных: key = arr[i] — элемент,
который нужно вставить в уже отсортированную часть
массива (arr[0..i-1]). left = 0 (начало отсортированной
части). right = i - 1 (конец отсортированной части).
b) Цикл поиска (while left <= right):
1. Вычисление середины: mid = left + (right - left) /
2
2. Если arr[mid] > key, то ищем в левой половине
(right = mid - 1).
3. Иначе ищем в правой половине (left = mid + 1).
c) Сдвигаем все элементы от left до i-1 вправо на 1
позицию.
d) Вставляем key на найденную позицию.
Итерация Текущий key Действие Состояние
4 9 Остаётся на
2. Маршруты выполнения основной программы
1) Старт программы: открытие файла numbers.txt для чтения данных.
2) Проверка наличия файла. Если файла нет, вывод сообщения об
ошибке и завершение работы программы.
[1, 3, 5, 8, 9, 2]

3) Подсчёт количества чисел и корректности данных. Если данные
данными
чисел
файле
483 647
чисел
483 647
из файла
127
1.1E+4932
при чтении файла
ский массив)
вставки
483 647
некорректны, вывод сообщения об ошибке и завершение
программы.
4) Выбор типа массива: пользователь выбирает между статическим
(фиксированного размера) и динамическим массивом.
5) Считывание данных. Для выбора массива пользователь вводит 1
или 2 соответственно.
6) Выделение памяти (для динамического массива) или
использование статического массива.
7) Заполнение массива только корректными числами из файла.
8) Закрытие файла.
9) Вызов функции сортировки методом бинарных вставок
10) Вывод отсортированного массива на экран.
11) Уведомление о наличии некорректных чисел (если такие
были).
12) Освобождение памяти (для динамического массива).
3. Описание переменных
Наименование Функциональное
значение
file Указатель на файл с
arr Указатель на массив
n Количество чисел в
valid_numbers Количество корректных
buffer Буфер для чтения чисел
number Переменная для
хранения числа
error Флаг наличия ошибок
Тип Диапазон
типа
FILE* — 8 байт
int* — 8 байт
int 0 до 2 147
int 0 до 2 147
char -128 до
long -3.4E-
4932 до
int 0 или 1 4 байта
Ёмкость
переменной
4 байта
4 байта
1 байт
8 байт
choice Выбор пользователя
key Текущий элемент для
(статический/динамиче
int 1 или 2 4 байта
int 1 до 2 147
4 байта

left, right, mid Индексы для бинарного
поиска
3. Блок-схемы
Основная программа main
int 0 до n-1 4 байта


Подпрограмма binary_insert_sort

4. Код программы
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <limits.h>
#include <errno.h>
// Функция сортировки бинарными вставками
// T = 1 + (n+1) + 2n + [∑(2 + 1 + 2 + 8log₂i + (3 + 3(i − left)) + 2)
// ≈ O(n²)
void binary_insert_sort(int *arr, int n) {
for (int i = 1; i < n; i++) { // 1 + (n+1) + 2n = 3n + 2
int key = arr[i]; // 2 (индексация + присваивание)
int left = 0; // 1 (присваивание)
int right = i - 1; // 2 (арифметика + присваивание)
while (left <= right) { // ⌈log₂i⌉ * 8
int mid = left + (right - left) / 2; // 4 (3 арифметики + присваивание)
if (arr[mid] > key) { // 2 (индексация + сравнение)
right = mid - 1; // 2 (арифметика + присваивание)
} else {
left = mid + 1; // 2 (арифметика + присваивание)
}
}
for (int j = i - 1; j >= left; j--) { // 2 + (i-left+1) + 2(i-left) = 3(i-left) + 3
arr[j + 1] = arr[j]; // 4 (индексация + присваивание)
}
arr[left] = key; // 2 (индексация + присваивание)
}
}
//T = 5 + 18n + 8 + 12 + 7 + 17n + 5 + 5n + 4 + 5 + O(n²) = 51 + 41n + O(n²)
int main() {
SetConsoleOutputCP(CP_UTF8); // 1 (вызов)
FILE *file = fopen("numbers.txt", "r"); // 1 (вызов)
if (!file) { // 1 (сравнение)
perror("Ошибка при открытии файла."); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
int *arr = NULL; // 1 (присваивание)
int n = 0; // 1 (присваивание)
int valid_numbers = 0; // 1 (присваивание)
char buffer[100]; // 1 (объявление)
long number;
int error = 0; // 1 (присваивание)
while (fscanf(file, "%99s", buffer) == 1) { // n итераций * 18 операций
] + 1

char *endptr; // 1 (объявление)
errno = 0; // 1 (присваивание)
number = strtol(buffer, &endptr, 10); // 3 (вызов + присваивание)
if (*endptr != '\0') { // 1 (сравнение)
printf("Ошибка: '%s' не является числом\n", buffer); // 1 (вызов)
error = 1; // 1 (присваивание)
} else if (errno == ERANGE || number < INT_MIN || number > INT_MAX) { // 3 (3 сравнения)
printf("Ошибка: число %s выходит за пределы типа int\n", buffer); // 1 (вызов)
error = 1; // 1 (присваивание)
} else {
valid_numbers++; // 2 (инкремент)
}
n++; // 2 (инкремент)
} // Итого: 18n
if (n <= 0) { // 1 (сравнение)
printf("Ошибка: файл пуст\n"); // 1 (вызов)
fclose(file); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
if (valid_numbers == 0) { // 1 (сравнение)
printf("Ошибка: в файле нет корректных чисел\n"); // 1 (вызов)
fclose(file); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
rewind(file); // 1 (вызов)
int choice; // 1 (объявление)
printf("Найдено %d чисел, из них %d корректных\n", n, valid_numbers); // 1 (вызов)
printf("Выберите способ хранения массива:\n"); // 1 (вызов)
printf("1. Статический массив (макс. 100 чисел)\n"); // 1 (вызов)
printf("2. Динамический массив\n"); // 1 (вызов)
printf("Введите номер выбора: "); // 1 (вызов)
if (scanf("%d", &choice) != 1 || (choice != 1 && choice != 2)) { // 4 (вызов + 3 сравнения)
printf("Неверный выбор.\n"); // 1 (вызов)
fclose(file); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
if (choice == 1) { // 1 (сравнение)
if (valid_numbers > 100) { // 1 (сравнение)
printf("Ошибка: для %d чисел требуется динамический массив\n", valid_numbers); // 1
(вызов)
fclose(file); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
static int static_arr[100]; // 1 (объявление)
arr = static_arr; // 1 (присваивание)

} else {
arr = (int *)malloc(valid_numbers * sizeof(int)); // 3 (арифметика + вызов + присваивание)
if (!arr) { // 1 (сравнение)
perror("Ошибка выделения памяти"); // 1 (вызов)
fclose(file); // 1 (вызов)
return EXIT_FAILURE; // 1 (возврат)
}
}
int index = 0; // 1 (присваивание)
while (fscanf(file, "%99s", buffer) == 1) { // 17n
char *endptr; // 1 (объявление)
errno = 0; // 1 (присваивание)
number = strtol(buffer, &endptr, 10); // 2 (вызов + присваивание)
if (*endptr == '\0' && errno != ERANGE && number >= INT_MIN && number <= INT_MAX) { // 7 (6
логических операций, разыменование)
arr[index++] = (int)number; // 5 (индексация + присваивание + инкремент +
преобразование)
}
}
fclose(file); // 1 (вызов)
if (index != valid_numbers) { // 1 (сравнение)
printf("Ошибка: несоответствие количества чисел\n"); // 1 (вызов)
if (choice == 2) free(arr); // 2 (сравнение + вызов)
return EXIT_FAILURE; // 1 (возврат)
}
binary_insert_sort(arr, valid_numbers); // O(n²)
printf("Отсортированный массив (%d элементов):\n", valid_numbers); // 1 (вызов)
for (int i = 0; i < valid_numbers; i++) { // 1 + (n+1) + 2*n = 3n + 2
printf("%d ", arr[i]); // 2 (индексация + вызов)
}
printf("\n"); // 1 (вызов)
if (choice == 2) { // 1 (сравнение)
free(arr); // 1 (вызов)
}
if (error) { // 1 (сравнение)
printf("Внимание: в файле были обнаружены некорректные числа\n"); // 1 (вызов)
}
return 0; // 1 (возврат)
}
5. Оценка сложности программы
Функция binary_insert_sort(): O(n²)

Внешний цикл: О(n).
Бинарный поиск: O(log n) на каждой итерации.
Сдвиг элементов: O(n) в худшем случае.
Итого: O(n²) в худшем случае (доминирует сдвиг элементов). В
лучшем случае, если массив отсортирован, сдвиг элементов не
требуется. Тогда сложность алгоритма O(n log n).
Основная программа main(): O(n²)
Чтение и проверка файла: O(n).
Заполнение массива: O(n).
Сортировка: O(n²) в худшем случае, O(n log n) в лучшем случае.
Вывод: O(n).
Общая сложность программы: O(n²) в худшем случае, O(n log n) в
лучшем случае (определяется сортировкой).
Было проведено 4 эксперимента на разном количестве элементов в
массиве (1000, 10 000, 50 000, 100 000):
Зависимость времени выполнения программы от количества чисел в
массиве представлена на графике:
Оценка времени
14
12
10
8
6
12,845672
ы
ы
ы
ы
4
2
0,002345
0
1000 10000 50000 100000
0,127831
На графике видно, что сложность программы равна O(n²), что
подтверждает теоретические расчёты.
3,214567
Соседние файлы в папке Лабы
