29
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ ЮЖНО-УРАЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ УДК 681.3.(07) К721 В. В. Костерин МЕТОДЫ ВНУТРЕННЕЙ СОРТИРОВКИ МАССИВОВ Учебное пособие к лабораторным работам Челябинск 2006

Методы внутренней сортировки массивов

Embed Size (px)

DESCRIPTION

учебно-методическое пособие к лабораторным работам «Методы внутренней сортировки массивов» Костерина В.В. можно рекомендовать для издания и включения в учебно-методический комплекс по дисциплинам «Алгоритмизация и программирование» и «Технологии программирования» в рамках учебной программы подготовки по специальности 080801 «Прикладная информатика (управление)» и «Прикладная информатика (юриспруденция)».

Citation preview

Page 1: Методы внутренней сортировки массивов

ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ

ЮЖНО-УРАЛЬСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

УДК 681.3.(07) К721

В. В. Костерин

МЕТОДЫ ВНУТРЕННЕЙ СОРТИРОВКИ МАССИВОВ

Учебное пособие к лабораторным работам

Челябинск

2006

Page 2: Методы внутренней сортировки массивов

Министерство образования и науки Российской Федерации Федеральное агентство по образованию

Южно-Уральский государственный университет Кафедра «Информационные системы»

УДК 681.3.(07) К721

В. В. Костерин

МЕТОДЫ ВНУТРЕННЕЙ СОРТИРОВКИ МАССИВОВ

Учебное пособие к лабораторным работам

Челябинск Издательство ЮУрГУ

2006

Page 3: Методы внутренней сортировки массивов

УДК 681.3.05(075.8) + 681.3.068(075.8) К721

Одобрено учебно-методической комиссией

факультета «Экономика и предпринимательство»

Рецензеты: В.Д. Гунченко, Сафронова И.В.

К721 Костерин, В.В. Методы внутренней сортировки массивов: Учебное пособие к лабораторным работам / В.В. Костерин – Челябинск: Изд-во ЮУрГУ, 2006 – 28 с.

Комплекс лабораторных работ условно разделен на две части: первая −

экспериментальное исследование трех базовых алгоритмов сортировки, вычислениепоказателей эффективности этих алгоритмов на различных выборках и сравнениеполученных результатов с теоретическими вычислениями; вторая часть посвященасозданию «мультфильмов», иллюстрирующих последовательность перемещенийэлементов одномерного целочисленного массива при сортировке ранеереализованными алгоритмами. Первая часть позволяет студентам освоитьклассические приемы создания алгоритмов методами структурного программирования, а вторая часть привносит значительную эмоциональную составляющую, которая способствует усвоению материала на подсознательномуровне.

Пособие предназначено для студентов специальности 351400 «Прикладнаяинформатика (управление, юриспруденция)».

УДК 681.3.05(075.8) + 681.3.068(075.8) © В.В. Костерин, 2006

© Издательство ЮУрГУ, 2006

Page 4: Методы внутренней сортировки массивов

3

ОГЛАВЛЕНИЕ

НЕМНОГО ТЕОРИИ: МЕТОДЫ ВНУТРЕННЕЙ СОРТИРОВКИ ................... 4

Сортировка включением..................................................................................... 5 Обменная сортировка.......................................................................................... 5 Сортировка выбором ........................................................................................... 6 Сравнение методов внутренней сортировки .................................................... 6

СОДЕРЖАНИЕ ЛАБОРАТОРНОЙ РАБОТЫ ..................................................... 7

Часть 1. Экспериментальное исследование алгоритмов ................................. 7 Последовательность выполнения работы ....................................................... 10 Часть 2. Создание демонстрационных программ-мультфильмов ................ 13 Последовательность выполнения работы ....................................................... 13

ОТЧЁТ О ЛАБОРАТОРНОЙ РАБОТЕ ............................................................... 18

ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ .................................................................. 19

БИБЛИОГРАФИЧЕСКИЙ СПИСОК.................................................................. 20

ПРИЛОЖЕНИЕ 1 .................................................................................................. 21

ПРИЛОЖЕНИЕ 2 .................................................................................................. 22

Page 5: Методы внутренней сортировки массивов

4

НЕМНОГО ТЕОРИИ: МЕТОДЫ ВНУТРЕННЕЙ СОРТИРОВКИ

«… Сортировка к тому же, еще и сама достаточно хороший пример задачи, которую можно решать с помощью многих различных алгоритмов. Каждый из них имеет и свои достоинства, и свои недостатки, и выбирать алгоритмы нужно исходя из конкретной постановки задачи.

В общем, сортировку следует понимать как процесс перегруппировки заданного множества объектов в некотором определенном порядке. Цель сортировки – облегчить последующий поиск элементов в таком отсортированном множестве. Это почти универсальная, фундаментальная деятельность. Мы встречаемся с отсортированными объектами в телефонных книгах, в списках подоходных налогов, в оглавлениях книг, в библиотеках, в словарях, на складах – почти везде, где нужно искать хранимые объекты. Даже малышей учат держать свои вещи «в порядке», и они уже сталкиваются с некоторыми видами сортировок задолго до того, как познакомятся с азами арифметики» [1].

Итак, задача сортировки ставится следующим образом: имеется последовательность однотипных записей − строк разделенных на поля, в которые можно записывать данные различных типов. Одно из полей записей выбрано в качестве ключевого, далее будем называть его ключом сортировки. Необходимо чтобы тип данных ключа допускал операции сравнения ("равно", "больше", "меньше", "не больше " и "не меньше"). Как правило, ключом сортировки являются данные целого типа. Задачей сортировки является преобразование исходной последовательности в последовательность, содержащую те же записи, но в порядке возрастания или убывания значений ключа. Метод сортировки называется устойчивым, если при его применении не изменяется относительное положение записей с равными значениями ключа.

Различают сортировку массивов записей, целиком расположенных в основной памяти – внутреннюю сортировку, и сортировку файлов, хранящихся во внешней памяти и не помещающихся полностью в основной памяти − внешнюю сортировку. Для внутренней и внешней сортировки требуются различные методы. В нашей лабораторной работе будем изучать наиболее известные, простые и понятные методы внутренней сортировки, используя средства языка программирования С в инструментальной среде Borland Builder C++.

Требованием, предъявляемые к внутренней сортировке является то, что методы не должны требовать дополнительной памяти: все перестановки с целью упорядочения элементов массива должны производиться в пределах того же массива. Мерой эффективности алгоритма внутренней сортировки являются число требуемых сравнений значений ключа (от английского Compare – C) и число перестановок элементов (от английского Move – M).

Page 6: Методы внутренней сортировки массивов

5

Заметим, что поскольку сортировка основана только на значениях ключа и никак не затрагивает оставшиеся поля записей, можно говорить о сортировке массивов ключей. Таким образом, задачу сортировки можно сформулировать следующим образом: задан одномерный целочисленный массив Arr[N] размером N = DIM, необходимо расположить элементы этого массива в порядке возрастания или убывания значений.

Сортировка включением Одним из наиболее простых и естественных методов внутренней сортировки

является сортировка простыми включениями. Идея алгоритма очень проста. Пусть имеется массив ключей Arr[0], Arr[1], ..., Arr[N–1]. Для каждого элемента массива, начиная со второго, производится сравнение с элементами с меньшим индексом. Элемент Arr[i] последовательно сравнивается с элементами Arr[j], где jЄ[i–1;0], т.е. изменяется от i–1 до 0. До тех пор, пока для очередного элемента Arr[j] выполняется соотношение Arr[j] > Arr[i], Arr[i] и Arr[j] меняются местами. Если удается встретить такой элемент Arr[j], что Arr[j] <= Arr[i], или если достигнута нижняя граница массива, производится переход к обработке элемента Arr[i+1]. Так продолжается до тех пор, пока не будет достигнута верхняя граница массива.

Легко видеть, что в лучшем случае, когда массив уже упорядочен для выполнения алгоритма с массивом из N элементов потребуется N–1 сравнение и 0 пересылок. В худшем случае, когда массив упорядочен в обратном порядке потребуется N(N–1)/2 сравнений и столько же пересылок. Таким образом, можно оценивать сложность метода простых включений как O(N2).

Можно сократить число сравнений, применяемых в методе простых включений, если воспользоваться тем, что при обработке элемента Arr[i] массива элементы Arr[0], Arr[1], ..., Arr[i–1] уже упорядочены, и воспользоваться для поиска элемента, с которым должна быть произведена перестановка, методом двоичного деления. В этом случае оценка числа требуемых сравнений становится O(NLogN). Заметим, что поскольку при выполнении перестановки требуется сдвижка на один элемент нескольких элементов, то оценка числа пересылок остается O(N2). Алгоритм сортировки включением, оформленный в виде функции приведен в файле UInsert.cpp и в приложении 1.

Обменная сортировка Простая обменная сортировка, называемая «методом пузырька», для массива

Arr[0], Arr[2], ..., Arr[N–1] работает следующим образом. Начиная с конца массива сравниваются два соседних элемента Arr[N–1] и Arr[N–2]. Если выполняется условие Arr[N–2] > Arr[N–1], то они меняются местами. Процесс продолжается для Arr[N–2] и Arr[N–3] и т.д., пока не будет произведено сравнение Arr[1] и Arr[0]. Понятно, что после этого на месте Arr[0] окажется элемент с наименьшим

Page 7: Методы внутренней сортировки массивов

6

значением. На втором шаге процесс повторяется, но последними сравниваются Arr[2] и Arr[1]. И так далее. На последнем шаге будут сравниваться только текущие значения Arr[N–1] и Arr[N–2]. Понятна аналогия с пузырьком, поскольку наименьшие элементы, самые «легкие», постепенно «всплывают» к верхней границе массива.

Для метода обменной сортировки требуется число сравнений N(N–1)/2, минимальное число пересылок 0, а среднее и максимальное число пересылок − O(N2).

Метод пузырька допускает три простых усовершенствования. Во-первых, если на некотором шаге не было произведено ни одного обмена, то выполнение алгоритма можно прекращать. Во-вторых, можно запоминать наименьшее значение индекса массива, для которого на текущем шаге выполнялись перестановки. Очевидно, что верхняя часть массива до элемента с этим индексом уже отсортирована, и на следующем шаге можно прекращать сравнения значений соседних элементов при достижении такого значения индекса. В-третьих, метод пузырька работает неравноправно для «легких» и «тяжелых» значений. Легкое значение попадает на нужное место за один шаг, а тяжелое на каждом шаге опускается по направлению к нужному месту на одну позицию.

Сортировка выбором При сортировке массива Arr[0], Arr[2], ..., Arr[N–1] методом простого выбора

среди всех элементов находится элемент с наименьшим значением Arr[i], и Arr[0] и Arr[i] обмениваются значениями. Затем этот процесс повторяется для получаемого подмассива Arr[1], Arr[2], ..., Arr[N–1], ... Arr[j], Arr[j+1], ..., Arr[N–1] до тех пор, пока мы не дойдем до подмассива Arr[N–1], содержащего к этому моменту наибольшее значение.

Для метода сортировки простым выбором оценка требуемого числа сравнений – N(N–1)/2. Порядок требуемого числа пересылок, которые требуются для выбора минимального элемента, в худшем случае составляет O(N2). Однако порядок среднего числа пересылок есть O(NLgN), что в ряде случаев делает этот метод предпочтительным.

Сравнение методов внутренней сортировки Для рассмотренных простых методов сортировки существуют точные

формулы, вычисление которых дает минимальное, максимальное и среднее число сравнений ключей C и пересылок элементов массива M. Таблица 1 содержит данные, приводимые в книге Никласа Вирта «Алгоритмы + данные = программы» [1].

Page 8: Методы внутренней сортировки массивов

7

Таблица 1

Характеристики простых методов сортировки

Min Avg Max

Прямое включение C

M

N–1

2(N–1)

(N2 + N – 2)/4

(N2 – 9N – 10)/4

(N2 –N)/2 – 1

(N2 –3N – 4)/2

Прямой выбор C

M

(N2 – N)/2

3(N–1)

(N2 – N)/2

N(Ln(N) + 0,57)

(N2 – N)/2

N2/4 + 3(N–1)

Прямой обмен C

M

(N2 – N)/2

0

(N2 – N)/2

0,75(N2 – N)

(N2 – N)/2

1,5(N2 – N)

СОДЕРЖАНИЕ ЛАБОРАТОРНОЙ РАБОТЫ

Лабораторная работа разделена на две части.

1. Экспериментальное исследование алгоритмов. Реализация вышеописанных алгоритмов и их тестовая проверка на трех различных массивах: упорядоченном, обратно упорядоченном и случайном, с подсчетом количества операций сравнений и перестановок.

2. Создание программ-мультфильмов, показывающих на экране монитора порядок сравнений и перестановок тремя вышеописанными методами.

Часть 1. Экспериментальное исследование алгоритмов

В результате выполнения этой части Вам необходимо:

1) заполнить экспериментальными данными сравнительную таблицу эффективности алгоритмов (табл. 2);

Page 9: Методы внутренней сортировки массивов

8

2) рассчитать теоретические значения показателей эффективности для массива вашего размера;

3) сравнить экспериментально полученные и расчетные значения; 4) высказать свое мнение об эффективности алгоритмов.

Таблица 2

Сравнительная таблица эффективности алгоритмов

Массив Показатель/Метод Insert Select Bubble

C

Упорядоченный

M

C

Обратно упорядоченный

M

Avr(C)

Случайный

Avr(M)

По структуре таблицы сравнительной эффективности легко видеть, что Вам необходимо проделать как минимум 9 опытов, в каждом из которых должны быть определены два числа C и M. Но в третьем случае, случайного массива, в отличие от двух первых, заранее нельзя предсказать значения показателей эффективности (смотри теорию выше). Естественно, что сравнительный анализ алгоритмов возможен только по средним значениям (в таблице Вы видите функцию Avr(C) – Average, что значит среднее значение С). Для этого необходимо провести серию опытов, желательно, 10–15 для каждого алгоритма, т.е. всего 30–45 опытов.

Page 10: Методы внутренней сортировки массивов

9

Именно это «жуткое» количество экспериментов и определяет структуру построения программы для реализации плана первой части лабораторной работы.

Итак, каждый алгоритм сортировки должен быть оформлен в виде самостоятельной программной единицы – функции, которая может быть использована многократно. Записать эти функции необходимо в различные файлы, например, UInsert.cpp, USelect.cpp и UBubble.cpp. Пример одной из них, метод простого включения, Вы найдете в файле UInsert.cpp и в приложении 1 в конце этого пособия, а вот оставшиеся две Вам надо написать самостоятельно. Обратите внимание на оформление текста программы и комментарии к алгоритму. Комментарии не позволят Вам забыть эти алгоритмы через неделю после сдачи отчета о своей работе. Кроме того, в отдельный файл (например, UMain.cpp) необходимо записать основную функцию main(), в которой реализован план части 1. Пример такой функции в файле UMain.cpp и в приложении 2 в конце пособия. Код несколько прямолинеен и, конечно, его можно оптимизировать, но за то он понятен, что сейчас самое главное.

Прочитаем внимательно комментарии, отбросив исходный код. Ниже приведен листинг программы без операторов языка «С» – только комментарии.

// Попытаемся открыть файл для сохранения результата // Если задано имя файла в качестве аргумента функции, то откроем его // Формируем картинку на экране для вывода показателей эффективности // 1) АНАЛИЗ УПОРЯДОЧЕННОГО МАССИВА // Формируем исходный массив и выводим его на экран // Последовательно обнуляем счетчики показателей эффективности // CCount, MCount и вызываем методы Select(...), Insert(...) и Bubble(...) // Результаты выводим на экран и при успешном открытии файла записываем

// Ожидаем реакции оператора. При нажатии <Esc> завершаем программу // 2) АНАЛИЗ ОБРАТНО УПОРЯДОЧЕННОГО МАССИВА // Формируем исходный массив и выводим его на экран // Последовательно обнуляем счетчики показателей эффективности // CCount, MCount и вызываем методы Select(...), Insert(...) и Bubble(...) // Результаты выводим на экран и при успешном открытии файла записываем.

// Ожидаем реакции оператора. При нажатии <Esc> завершаем программу // 3) АНАЛИЗ СЛУЧАЙНОГО МАССИВА

Page 11: Методы внутренней сортировки массивов

10

// Создаем цикл для нескольких реализаций случайного массива // Формируем исходный массив и выводим его на экран // Последовательно обнуляем счетчики показателей эффективности // CCount, MCount и вызываем методы Select(...), Insert(...) и Bubble(...) // Результаты выводим на экран и при успешном открытии файла записываем

// Ожидаем реакции оператора. При нажатии <Esc> завершаем цикл // Перед завершением программы закрываем файл и очищаем буфер клавиатуры

Легко видеть, что в функции main(…) решены нижеследующие задачи. 1. Распределение памяти для сохранения массив и показателей

эффективности; 2. Автоматическое формирование массивов для всех 3-х экспериментальных

случаев, а именно, упорядоченного, обратно упорядоченного и случайного; 3. Запись результатов работы программы на экран монитора и во внешний

файл, если его имя задано в качестве параметра программы; 4. Последовательный вызов всех методов для случаев упорядоченного и

обратно упорядоченного массивов; 5. Для случайного массива организован управляемый пользователем цикл,

который позволяет получить требуемое количество реализаций случайной последовательности.

Обратите внимание на операторы препроцессора #include в начале файла. С помощью них при компиляции описываются используемые функции ввода/вывода, разработанные программистами фирмы Borland, например, cprintf(…), gotoxy(…), randomize(…) и другие. Подробно не будем останавливаться на описании применения этих функций. Воспользуйтесь справочной системой C++Builder. Для этого подведите маркер к имени функции в тексте программы и одновременно нажмите клавиши <Ctrl> и <F1>, а потом разбирайтесь самостоятельно, используя свои знания английского языка (можно со словарем).

Последовательность выполнения работы Если Вам все понятно и на компьютере есть Borland C++Builder (желательно

версии 6, но можно и другой), то можно приступать к практическим упражнениям. Но прежде, на локальном диске создайте свою папку, например, с именем «Лабораторная работа (Методы сортировки)» для выполнения работы и скопируйте в нее все файлы приложения к этому пособию.

Page 12: Методы внутренней сортировки массивов

11

Рис. 1. Окно мастера создания приложения

Загрузите C++Builder и, не обращая внимание на возникшие перед глазами заготовку формы Form1, инспектор объектов Object Inspector и проводник объектов Object TreeView, выполните следующую последовательность пунктов главного меню File->New->Other. На закладке New возникшего диалогового окна (рис. 1) New Items выберите работу, которую должен выполнить Builder, а именно, Console Wizard, т.е. запустить мастер создания консольных приложений (такие приложения, которые могут работать под DOS без использования надстройки Windows). После того, как Вы выберите стиль написания программы «С» или «С++» (рис. 2) на экране появится окно редактора файла Unit1 с заготовкой функции main (рис. 3).

Рис. 2. Окна выбора стиля исходного кода

Page 13: Методы внутренней сортировки массивов

12

Рис. 3. Окно редактора исходного кода

Создание проекта консольного приложения свершилось. Зафиксируйте этот факт и сохраните его результаты, воспользовавшись главным меню File->Save All. С++Builder автоматически формирует имена всех основных и служебных файлов, например, Unit1.cpp или Project1.bpf. Однако, проявите свою индивидуальность и в окнах диалога сохранения поменяйте их на более оригинальные.

Теперь самое время воспользоваться готовеньким и подключить файлы UInsert.cpp и UMain.cpp в свой проект. Используйте пункты главного меню Project->Add to Project… и в диалоговом окне выберите файлы UMain.cpp и UInsert.cpp. При этом, в окне редактора появятся закладки с исходным текстов функций. А вот заготовку, которую создал С++Builder, из проекта надо убрать – это меню Project->Remove from Project…

Дело за Вами, воспользуйтесь File->New->Other->File C для записи своих функций реализующих алгоритмы двух оставшихся методов. Обратите внимание на интерфейс этих функций – он описан в файле UMain.cpp.

Если все правильно, задайте параметры загрузки Run->Parameters, имя файла сохранения, например, Sorting.rez (но можно и другое, более близкое вашему сердцу), и смело жмите кнопку <F9> для экспериментирования. Прочитав файл

Page 14: Методы внутренней сортировки массивов

13

Sorting.rez, Вы сможете заполнить сравнительную таблицу эффективности и считать первый пункт плана экспериментального исследования алгоритмов выполненным.

Оставшиеся три последних пункта первой части Вам необходимо отразить в отчете, а именно пункты:

2) рассчитать теоретические значения показателей эффективности для массива вашего размера. Проще всего это сделать с использованием MS Exell пакета MS Office, для различных значений размера массива;

3) сравнить экспериментально полученные и расчетные значения; 4) высказать свое мнение об эффективности алгоритмов и правильности

теоретических формул.

Часть 2. Создание демонстрационных программ-мультфильмов Прежде чем приступить к выполнению этой части лабораторной работы и,

если Вы ещё не «прониклись» идеями простых алгоритмов сортировки, запустите исполняемые программы из электронного приложения этого пособия, а именно: PASelect.exe, PAInsert.exe и PABubble.exe.

Посмотрели? Хотелось бы, чтобы Вы самостоятельно создали нечто подобное. И все не так страшно, как кажется с первого взгляда на исходный текст программы в файле UAInsert.cpp (приложение 2).

Последовательность выполнения работы Итак, с чего начать? Обсудим последовательность этой части работы на

примере файла UAInsert.cpp. Создайте новое консольное приложение и сохраните его в отдельной папке с

оригинальными именами файлов. Как это делать Вы уже знаете. Аргументы функции main() нам не нужны, поэтому уверенно их удаляем.

Начинаем проектировать программу и записывать её конструкции между открывающей фигурной скобкой и оператором return заготовки. Определимся со структурой программы.

1. Необходимо сформировать на экране статическое изображение, на фоне которого будут развиваться дальнейшие события;

2. Организовать управляемый зрителем цикл для возможности неоднократного просмотра мульфильма.

Обычная конструкция типа «Последовательность». Для начала нарисуем неизменные элементы экрана, который мы хотим видеть во время исполнения

Page 15: Методы внутренней сортировки массивов

14

программы. Картинку моделируем с помощью массива строковых констант, инициализирующих указатель на символьные массивы:

char *Screen[] = { “…”, “…”, …, NULL };

Здесь Вы в полной мере можете проявить свою фантазию. Символьный массив массивов создан, побеспокоимся о его появлении на экране. Обратите внимание, массив Screen[] завершается константой NULL, которая определена в файле <stdio.h>. Именно этот признак завершения массива будем использовать условием завершения цикла вывода строк на экран с помощью специальных функций вывода (кстати, их описание находится в файле conio.h). Это удобно с той точки зрения, что в ходе разработки Вы сможете менять количество строк массива моделирующего экран, не изменяя исполняемый код. Запишем его:

int i; for( i=0; Screen[i]; i++ ) { gotoxy( LMARG, 1+i ); cprintf( “%s”, Screen[i] ); }

или int i=0; while( Screen[i] ) { gotoxy( LMARG, 1+i ); cprintf( “%s”, Screen[i++] ); }

По своей работе эти конструкции равноценны. Screen[i] – является условием завершения циклов while-do и for-do. Т.е. при изменение параметра i++ и в for-do и в while-do мы доберемся до Screen[i] == NULL − цикл завершится. Вы уже должны знать как работают функции вывода gotoxy(…) и cprintf(…). Функция gotoxy( LMARG, 1+i ) управляет положением маркера на экране. Здесь LMARG – левая граница картинки на экране, она определена с помощью оператора препроцессора, 1+i – номер строки, значение которого изменяется в цикле. Функция cprintf( “%s”, Screen[i] ) выводит i-ю строку массива Screen[] на экран начиная с указателя маркера, заданного gotoxy(…). Конструкции for-do и while-do – конструкции, определенные синтаксисом языка «С». Если дописать ещё один оператор – вызов функции

getch();

Page 16: Методы внутренней сортировки массивов

15

− это опрос буфера клавиатуры, и нажать клавишу <F9>, то уже можно наблюдать результат своих усилий. Проверьте, подкорректируйте картинку и дальше…

Уберем из текста последний записанный оператор getch(); и организуем управляемый зрителем цикл. Заметим, что количество повторений мультфильма нам не известно. Что взбредет в голову этому зрителю? Поэтому в качестве циклической конструкции выберем while-do и дополним её обвязкой из необходимых операторов:

int ch, Stop=0; while( !Stop ) { // Тело цикла ch = getch(); if( ch == 27 ) Stop = 1; }

С языка «С» на русский это переводится очень просто: пока НЕ-Stop выполнить тело цикла, в переменную ch записать код символа, нажатого на клавиатуре и если он равен 27, а это код символа Esc, присвоить Stop значение 1 (Истина), что прервет выполнения цикла while (!Stop – НЕ-Истина есть Ложь); в противном случае повторить тело цикла и опрос клавиатуры. Если Вы нажмете <F9> и запустите созданное приложение, то до тех пор, пока Вы не нажмете клавишу <Esc>, на экране будет оставаться нарисованная Вами статичная картинка. Каркас программы создан и не забудьте сохранить его на диске.

Приступим к синтезу тела цикла. Оно также представляет собой последовательность:

1) формируем случайным образом значения элементов исходного массива и показываем их в отведенном месте статичной картинки;

2) показываем мультфильм, иллюстрирующий алгоритм. В нашем случае – это метод включения.

Что касается пункта 1), то этот код можно скопировать из файла UMain.cpp (см. приложение 2) – это уже известно, и немного изменить в часть определения знакомест записи чисел на экране. Эти значения, аргументы функции gotoxy(…), определяются Вашей статичной картинкой:

randomize(); for( i=0; i<DIM; i++ ) { Arr[i] = random( 100 ) – 50; gotoxy( LMARG+1 + 6*i, ARR );

Page 17: Методы внутренней сортировки массивов

16

cprintf( “%3d”, Arr[i] ); }

Не забудьте определить оператором препроцессора #define значение ARR – номер строки, в которой нарисуем элементы массива, и описать массив Arr[DIM]. Вставьте этот код после открывающей скобки конструкции while-do. Вот только randomize() надо вынести за скобки. Дело в том, что датчик случайных чисел в программе можно инициализировать только один раз и если это будет повторяться в цикле, то получаться одна и та же последовательность. А мы хотим получить несколько различных реализаций случайного массива. В результате получим:

int ch, Stop=0; #define ARR 5 int Arr[DIM]; randomize(); while( !Stop ) { for( i=0; i<DIM; i++ ) { Arr[i] = random( 100 ) – 50; gotoxy( LMARG+1 + 6*i, ARR ); cprintf( “%3d”, Arr[i] ); } ch = getch(); if( ch == 27 ) Stop = 1; }

Пункт 2) структуры программы, то для чего все это затевается, полностью определяется алгоритмом, реализующим метод сортировки. Из файла UInsert.cpp скопируем тело функции Insert, немного подправим и получим:

int ch, Stop=0; #define ARR 5 int Arr[DIM]; int j; randomize(); while( !Stop ) {

Page 18: Методы внутренней сортировки массивов

17

for( i=0; i<DIM; i++ ) { Arr[i] = random( 100 ) – 50; gotoxy( LMARG+1 + 6*i, ARR ); cprintf( “%3d”, Arr[i] ); } getch(); // В дальнейшем удалить эту строку for( i=1; i<DIM; i++ ) { Temp = Arr[i];, for( j=i-1; j >= 0; j-- ) { if( Arr[j] > Temp ) { Arr[j+1] = Arr[j]; Arr[j] = Temp; } else { break; } }

}

for( i=0; i<DIM; i++ ) { // В дальнейшем удалить эту строку gotoxy( LMARG+1 + 6*i, ARR ); // В дальнейшем удалить эту строку cprintf( “%3d”, Arr[i] ); // В дальнейшем удалить эту строку

} // В дальнейшем удалить эту строку ch = getch(); if( ch == 27 )

Stop = 1; }

Запустите приложение и убедитесь в том, что Вы получаете различные реализации случайного массива, а алгоритм сортировки исправно делает своё дело.

Приступим к «оживлению» нашей статичной картинки. Прежде всего, удалим из текста закомментированные строки и разберемся с понятием мультипликация в применении к нашему приложению.

Page 19: Методы внутренней сортировки массивов

18

Нам необходимо показать последовательность перемещений элементов массива, а это движение, которое определяется каждым оператором алгоритма сортировки. Например, записан оператор: Temp = Arr[i]; т.е. мы запоминаем значение Arr[i] в переменной Temp, чтобы не потерять его при замещении i-го места (i-1)-ым элементом. Как отразить этот факт? А переместим Arr[i] элемент на строку вверх или вниз. Делается это очень просто: при перемещении вниз на место Arr[i] с координатами экрана [X1;Y1] записываются пробелы, которые закроют все цифры числа, а значение Arr[i] записать в точку с координатами [X1;Y1+1]. Для того чтобы зафиксировать это надо немного подождать, а потом повторить эту операцию требуемое число раз. Итак, одно перемещение числа, программно, выглядит следующим образом:

gotoxy( LMARG+i*6, ARR ); cprintf( " " ); gotoxy( LMARG+i*6, DOWN ); cprintf( "%3d", Temp ); MyDelay( WAIT );

Понятно? На алгоритм функции MyDelay(…) можно пока не обращать внимания, надо только знать, что WAIT – это задержка в сотых долях секунды (100 – 1 с).

В качестве выразительных средств нашего мультфильма при текстовом режиме работы монитора можно, кроме перемещения, применить изменение цвета выводимого образа и «подмаргивание». Оба эти эффекта легко реализуются с помощью функции textcolor( int color ). Для подмаргивания необходимо прибавить к задаваемому цвету мнемонику BLINK, например, textcolor( YELLOW+BLINK ) – желтый мерцающий.

А теперь запустите приложение PAInsert.exe и, глядя на текст, записанный на экране, сопоставьте изобразительные средства и операторы алгоритма. Внимательно изучите текст файла UAInsert.cpp и надеюсь все станет более-менее понятно.

Вот, пожалуй, и все, что можно порекомендовать перед началом создания учебного мультфильма. Теперь дело за Вашей фантазией. Успехов …

ОТЧЁТ О ЛАБОРАТОРНОЙ РАБОТЕ

Надеюсь, фантазия Вас не подвела и Вы успешно справились с заданием – пора подумать об отчёте и вспомнить преподавателя. Итак, в качестве отчетных материалов необходимо предоставить нижеследующее.

Page 20: Методы внутренней сортировки массивов

19

1. Красиво оформленные исходные тексты Ваших программ, выполненные в едином стиле. О стиле записи исходных текстов прочитайте в работе [2] библиографии. Не забывайте, что каждый файл должен обязательно содержать «шапку», в которой указываются имя проекта, имя файла, номер версии, автор и его учебная группа, комментарии с кратким описание модуля, все файлы для совместной сборки программы, даты создания и последней модификации кода. Не забывай комментировать текст программы. По первой части таких файлов должно быть четыре: 3 функции, реализующие алгоритмы сортировки и общая программа для получения статистики при экспериментировании;

2. Загружаемые программы, собранные без подключения пакетов и run-time библиотек. Всего таких файлов 4;

3. Отчет с обработкой результатов экспериментов в формате MS Office и выводами о сравнении теории с практикой;

4. Для второй части работы Вы должны, также предоставить три файла с исходными текстами программ-мультфильмов и три загружаемых программы. Помните, что программа может находиться только в двух состояниях: 1) или «работает», или 2) «не работает». Понятие «работает чуть-чуть» не существует, как не существует сыра второго сорта. Вы создавали мультфильмы, т.е., в некотором роде, художественные произведения. Поэтому побеспокойтесь об эстетическом восприятии Ваших шедевров: цветах, расположении на экране выразительных элементов, комментариях и прочее, прочее… Не забывайте о минимизации операций пользователя Ваших программ и не заставляйте его лишний раз нажимать на кнопочки клавиатуры.

5. По результатам работы проводиться зачетный тест. Примерные вопросы теста приведены ниже в разделе вопросы для самоконтроля.

ВОПРОСЫ ДЛЯ САМОКОНТРОЛЯ

1. Какие аргументы имеет функция main(…)? 2. Перечислите исполняемые конструкции языка C; 3. Назовите базовые типы данных языка C. Как описываются переменные и

постоянные? 4. С помощью какой программы записывается исходный текст на языке C? 5. Какая программа в системе C++ выполняется перед началом этапа

компиляции? 6. Какая программа объединяет результат работы компилятора с

различными библиотечными функциями, чтобы создать исполняемый загрузочный модуль?

7. Какая программа загружает исполняемый модуль с диска в память? 8. С какой функции начинается каждая программа на C?

Page 21: Методы внутренней сортировки массивов

20

9. С какой скобки начинается тело каждой функции? 10. Какой скобкой заканчивается тело каждой функции? 11. Каким знаком заканчивается каждый оператор? 12. Какой оператор используется для принятия решений? 13. Верно или неверно утверждение: «Все переменные должны быть

объявлены до того, как они используются.»? 14. Верно или неверно утверждение: «Всем переменным, когда они

объявляются, должен быть присвоен тип.»? 15. Верно или неверно утверждение: «C++ рассматривает переменные

number и NuMbEr как одинаковые.»? 16. Верно или неверно утверждение: «Объявления в теле функции C++

могут появляться почти везде.»? 17. Верно или неверно утверждение: «Операция вычисления остатка (%)

может быть использована только с целыми числами.»?

18. Верно или неверно утверждение: “Все арифметические операции *, /, %, + и — имеют одинаковый уровень приоритета.”?

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1. Вирт, Н. Алгоритмы и структуры данных / Н. Вирт. − М: Изд-во «Мир», 1989. − 360 с.;

2. Дейтел, Х. Как программировать на С++ / Х.Дейтел, П.Дейтел. − М: Изд-во «Мир», 2000. − 1011 с.

Page 22: Методы внутренней сортировки массивов

21

ПРИЛОЖЕНИЕ 1

/* Project Name : PSorting File Name : UInsert.cpp Create Date : 1.10.04 Modify Date : 5.10.04 Version : 1.0 Autor : (C) Vadim V. Kosterin, Chelyabinsk, 2004 Comment : Лабораторная работа № 1 (часть 1) Метод прямого включения для сортировки одномерного целочисленного массива. */ void Insert( int *Arr, // Адрес массива для сортировки и сохранения // результата. int Dim, // Размер массива. int *CCount, // Адрес переменной для сохранения количества // операций сравнения. int *MCount ) // Адрес переменной для сохранения количества // операций обмена. Если CCount = MCount = NULL, // то они не используются. { int i, j, Temp; for( i=1; i<Dim; i++ ) { // Основной цикл со 2-го элемента право Temp = Arr[i]; // Запомним элемент для сравнения for( j=i-1; j >= 0; j-- ) { // Ищем влево ближайший меньший // элемент if( CCount ) (*CCount)++; // Считаем операции сравнения if( Arr[j] > Temp ) { if( MCount ) (*MCount)++; // Считаем операции перестановки Arr[j+1] = Arr[j]; // Сдвигаем элемент влево, а на его // место ставим сравниваемый элемент Arr[j] = Temp; } else { break; // Нашли ближайшее меньшее, // завершим цикл } // по j } } return; // Выход в вызывающую функцию }

Page 23: Методы внутренней сортировки массивов

22

ПРИЛОЖЕНИЕ 2

/* Project Name : PSorting File Name : UMain.cpp Create Date : 1.10.04 Modify Date : 6.10.04 Version : 1.0 Autor : (C) Vadim V. Kosterin, Chelyabinsk, 2004 Files to Link : USelect.cpp, UInsert.cpp, UBubble.cpp Comment : Лабораторная работа № 1 (часть 1) Основная функция main для анализа эффективности трех методов

сортировки: 1) прямого выбора; 2) прямого включения и 3) прямого обмена (метод "пузырька"). В качестве исходных данных используются три массива сформированные как: 1) упорядоченный; 2) обратноупорядоченный; 3) случайный. Для случайного массива можно получить несколько реализаций. Результаты выводятся на экран монитора, а значения счетчиков количества сравнений и перестановок записываются в файл SortingAnalysiz.rez и в дальнейшем используются для расчета средних значений показателей эффективности. Проект выполнен с использованием Borland C++ Builder 6.0 */ //--------------------------------------------------------------------------- // Подключим файлы с описание используемых встроенных функций Builder C++ #include <vcl.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused #define DIM 15 // Размер массива

Page 24: Методы внутренней сортировки массивов

23

#define LMARG 5 // Левая граница вывода на экран #define FALSE 0 // Ложь #define TRUE 1 // Истина // Описание функций, реализующих три алгоритма сортировки void Select( int *Arr, int Dim, int *CCount, int *MCount ); void Insert( int *Arr, int Dim, int *CCount, int *MCount ); void Bubble( int *Arr, int Dim, int *CCount, int *MCount ); int main( int argc, char *argv[] ) { int ArrS[DIM], ArrI[DIM], ArrB[DIM], // Массивы для сортировки i, // Служебная переменная CCount, MCount; // Показатели эффективности

int stop = FALSE, // Индикатор завершения программы ch, // Код клавиши клавиатуры CicleCount = 1; // Счетчик реализаций случайного массива

FILE *out = NULL; // Файл для созранения результата

// Попытаемся открыть файл для сохранения результата if( argc > 1 ) { // Если задано имя файла - откроем его

if( ( out = fopen( argv[1], "a+" ) ) == NULL ) { printf( "Cannot open file %s. Result not saved ...\n\nPress any key to continue ...", strupr(argv[1]) );

getch(); } } // Формируем картинку на экране для вывода показателей эффективности gotoxy( LMARG, 1 ); cprintf( "Analysiz of sorting's metodes" ); gotoxy( LMARG, 11 ); cprintf( "-------------- Random Serial BackSerial" ); gotoxy( LMARG, 12 ); cprintf( "Select: CCount ???? ???? ????" ); gotoxy( LMARG, 13 ); cprintf( " MCount ???? ???? ????" ); gotoxy( LMARG, 14 ); cprintf( "Insert: CCount ???? ???? ????" ); gotoxy( LMARG, 15 ); cprintf( " MCount ???? ???? ????" ); gotoxy( LMARG, 16 ); cprintf( "Bubble: CCount ???? ???? ????" );

Page 25: Методы внутренней сортировки массивов

24

gotoxy( LMARG, 17 ); cprintf( " MCount ???? ???? ????" ); gotoxy( 40-strlen("Press any key to continue or <Esc> to exit ...")/2, 24 ); cprintf( "Press any key to continue or <Esc> to exit ..." ); // 1) АНАЛИЗ УПОРЯДОЧЕННОГО МАССИВА // Формируем исходный массив и выводи его на экран gotoxy( LMARG, 5 ); cprintf( "Serial:" ); for( i=0; i<DIM; i++ ) { ArrS[i] = 2*i + DIM; ArrI[i] = ArrS[i]; ArrB[i] = ArrS[i]; gotoxy( strlen( "Backserial: ")+LMARG+i*4, 5 ); cprintf( "%3d", ArrS[i] ); } // Последовательно обнуляем счетчики показателей эффективности CCount, MCount и // вызываем методы Select(...), Insert(...) и Bubble(...). Результаты // выводим на экран и при успешном открытии файла записываем. if( out ) { fprintf( out, "Анализ эффективности алгоритмов сортировки\n\nУПОРЯДОЧЕННЫЙ МАССИВ\n CCount MCount CCount+MCount\n" );

} CCount = 0; MCount = 0; Select( ArrS, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 12 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 13 ); cprintf( "%4d", MCount ); if( out ) { fprintf( out, "Метод прямого выбора: %3d %3d %3d\n", CCount, MCount, CCount+MCount ); } CCount = 0; MCount = 0; Insert( ArrI, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 14 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 15 ); cprintf( "%4d", MCount ); if( out ) {

Page 26: Методы внутренней сортировки массивов

25

fprintf( out, "Метод прямого включения: %3d %3d %3d\n", CCount, MCount, Count+MCount ); } CCount = 0; MCount = 0; Bubble( ArrB, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 16 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? "), 17 ); cprintf( "%4d", MCount ); if( out ) { fprintf( out, "Метод прямого обмена: %3d %3d %3d\n", CCount, MCount, Count+MCount ); } for( i=0; i<DIM; i++ ) { gotoxy( strlen( "Backserial: ")+LMARG+i*4, 6 ); cprintf( "%3d", ArrI[i] ); } // Ожидаем реакции оператора. При нажатии <Esc> завершаем программу // Функция exit(...) закрывает все файлы и очищает все буферы программы if( ( ch = getch() ) == 27 ) exit( EXIT_SUCCESS ); // 2) АНАЛИЗ ОБРАТНОУПОРЯДОЧЕННОГО МАССИВА // Формируем исходный массив и выводи его на экран if( out ) { fprintf( out, "\n\nОБРАТНОУПОРЯДОЧЕННЫЙ МАССИВ\n CCount MCount Count+MCount\n" ); } gotoxy( LMARG, 7 ); cprintf( "Backserial: " ); for( i=0; i<DIM; i++ ) { ArrS[i] = DIM - i; ArrI[i] = ArrS[i]; ArrB[i] = ArrS[i]; gotoxy( strlen( "Backserial: ")+LMARG+i*4, 7 ); cprintf( "%3d", ArrS[i] ); } // Последовательно обнуляем счетчики показателей эффективности CCount, MCount и // вызываем методы Select(...), Insert(...) и Bubble(...). Результаты // выводим на экран и при успешном открытии файла записываем.

Page 27: Методы внутренней сортировки массивов

26

CCount = 0; MCount = 0; Select( ArrS, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 12 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 13 ); cprintf( "%4d", MCount ); if( out ) { fprintf(out,"Метод прямого выбора: %3d %3d %3d\n", CCount, MCount, Count+MCount); } CCount = 0; MCount = 0; Insert( ArrI, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 14 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 15 ); cprintf( "%4d", MCount ); if( out ) {

fprintf( out, "Метод прямого включения: %3d %3d %3d\n", CCount, Count, CCount+MCount );

} CCount = 0; MCount = 0; Bubble( ArrB, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 16 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount ???? ???? "), 17 ); cprintf( "%4d", MCount ); if( out ) { fprintf( out, "Метод прямого обмена: %3d %3d %3d\n", CCount, MCount, Count+MCount ); } for( i=0; i<DIM; i++ ) { gotoxy( strlen( "Backserial: ")+LMARG+i*4, 8 ); cprintf( "%3d", ArrI[i] ); } // Ожидаем реакции оператора. При нажатии <Esc> завершаем программу. // Функция exit(...) закрывает все файлы и очищает все буферы программы if( ( ch = getch() ) == 27 ) exit( EXIT_SUCCESS ); // 3) АНАЛИЗ СЛУЧАЙНОГО МАССИВА gotoxy( LMARG, 3 );

Page 28: Методы внутренней сортировки массивов

27

cprintf( "Random:" ); randomize(); // Для получения нескольких случайных реализаций делаем цикл if( out ) { fprintf( out, "\n\nСЛУЧАЙНЫЙ МАССИВ" ); } while( !stop ) { // Формируем исходный массив и выводи его на экран if( out ) { fprintf(out,"\n\nРеализация%3d-------------------------------------------- \n",CicleCount); } for( i=0; i<DIM; i++ ) { ArrS[i] = random( 100 ) - 50; ArrI[i] = ArrS[i]; ArrB[i] = ArrS[i]; gotoxy( strlen( "Backserial: ")+LMARG+i*4, 3 ); cprintf( "%3d", ArrS[i] ); if( out ) fprintf( out, "%3d ", ArrS[i] ); } if( out ) fprintf( out, "\n CCount MCount CCount+MCount\n" ); // Последовательно обнуляем счетчики показателей эффективности CCount, MCount и // вызываем методы Select(...), Insert(...) и Bubble(...). Результаты // выводим на экран и при успешном открытии файла записываем. CCount = 0; MCount = 0; Select( ArrS, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount "), 12 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount "), 13 ); cprintf( "%4d", MCount ); if( out ) {

fprintf( out, "Метод прямого включения: %3d %3d %3d\n", CCount, Count, Count+MCount );

} CCount = 0; MCount = 0; Insert( ArrI, DIM, &CCount, &MCount );

Page 29: Методы внутренней сортировки массивов

28

gotoxy( LMARG+strlen("Select: CCount "), 14 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount "), 15 ); cprintf( "%4d", MCount ); if( out ) {

fprintf( out, "Метод прямого включения: %3d %3d %3d\n", CCount, Count, CCount+MCount );

} CCount = 0; MCount = 0; Bubble( ArrB, DIM, &CCount, &MCount ); gotoxy( LMARG+strlen("Select: CCount "), 16 ); cprintf( "%4d", CCount ); gotoxy( LMARG+strlen("Select: CCount "), 17 ); cprintf( "%4d", MCount ); for( i=0; i<DIM; i++ ) { gotoxy( strlen( "Backserial: ")+LMARG+i*4, 4 ); cprintf( "%3d", ArrI[i] ); } if( out ) {

fprintf( out, "Метод прямого обмена: %3d %3d %3d\n", CCount, MCount, Count+MCount ); } if( out ) { fprintf( out, "\nРезультат по реализации %3d \n", CicleCount ); for( i=0; i<DIM; i++ ) { fprintf( out, "%3d ", ArrS[i] ); } } gotoxy( LMARG, 19 ); cprintf( "Random: TestCount %4d", CicleCount++ ); // Ожидаем реакции оператора. При нажатии <Esc> завершаем цикл ch = getch(); if( ch == 27 ) stop = TRUE; } // Перед завершением программы закрываем файл и очищаем буфер клавиатуры

exit( 0 ); // Эта функция все "подчистит" и сделает все что надо return 0; }