Методы сортировки

Автор работы: Пользователь скрыл имя, 14 Мая 2013 в 04:55, реферат

Описание работы

Цель данной работы: изучить различные методы сортировки; научиться проводить сравнительный анализ методов друг с другом и выбирать наиболее подходящий для конкретных целей алгоритм сортировки.
Сортировка - это процесс упорядочения некоторого множества элементов, на котором определены отношения порядка >, <, ³ , £ . Когда говорят о сортировке, подразумевают упорядочение множества элементов по возрастанию или убыванию. Рассмотрим различные алгоритмы сортировки и выясним, почему возникла необходимость появления такого большого числа алгоритмов решения одной и той же задачи.

Файлы: 1 файл

Про сложность алгоритмов.docx

— 84.89 Кб (Скачать файл)

Лабораторная  работа №1

Методы сортировки

1. Цель работы

Цель данной работы:

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

2. Краткие сведения  из теории

2.1. Значимость  и суть задачи

Данный раздел посвящен сортировке: назначению, различным методам, их сравнительному анализу.

Сортировка - это процесс упорядочения некоторого множества элементов, на котором определены отношения порядка >, <, ³ , £ . Когда говорят о сортировке, подразумевают упорядочение множества элементов по возрастанию или убыванию. Рассмотрим различные алгоритмы сортировки и выясним, почему возникла необходимость появления такого большого числа алгоритмов решения одной и той же задачи.

Вообще, алгоритмы сортировки - одна из самых хорошо исследованных  областей информатики. Тем не менее  не исключены открытия и в этой области, потому что наверняка существуют еще какие-то пока неизвестные методы сортировки, основанные на новых принципах  и идеях.

Алгоритмы сортировки имеют  большое практическое применение. Их можно встретить почти везде, где речь идет об обработке и хранении больших объемов информации. Некоторые  задачи обработки данных решаются проще, если данные упорядочены. Примеры таких  задач мы рассмотрим в этой лабораторной работе.

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

2.2. Методы сортировки

Традиционно различают внутреннюю сортировку, в которой предполагается, что данные находятся в оперативной памяти, и важно оптимизировать число действий программы (для методов, основанных на сравнении, число сравнений, обменов элементов и пр.), и внешнюю, в которой данные хранятся на внешнем устройстве с медленным доступом (магнитные лента, барабан, диск) и прежде всего надо снизить число обращений к этому устройству.

В этой работе рассмотрим алгоритмы  внутренней сортировки массивов элементов, так как они более важны  большинству программистов в  повседневной практике.

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

2.2.1. О сложности  алгоритмов

При рассмотрении методов  будем оперировать понятиями  временной T и пространственной O теоретической  сложности (в дальнейшем будем опускать слово «теоретическая») алгоритма, поэтому вкратце упомянем, что  это такое.

Сложность алгоритма - одночлен, отражающий порядок величины требуемого ресурса (времени/дополнительной памяти) в зависимости от размерности задачи.

Например, если число тактов (действий), необходимое для работы метода, выражается как 11*n2+19*n*log(n)+3*n+4, где n-размерность задачи, то мы имеем дело с алгоритмом со сложностью T(n2). Фактически, мы из всех слагаемых оставляем только слагаемое, вносящее наибольший вклад при больших n (в этом случае остальными слагаемыми можно пренебречь) и игнорируем коэффициент перед ним.

В большинстве случаев  оказывается довольно трудно найти  точную практическую сложность алгоритма (функцию от n, позволяющую точно  определить требуемое время работы/объем  памяти). Однако, теоретическую сложность  найти можно и это достаточно просто.

Временная (пространственная) сложность не позволяет определить время (необходимый объем дополнительной памяти) работы алгоритма, но она позволяет  оценить динамику роста времени (объема дополнительной памяти), необходимого для работы метода, при увеличении размерности задачи. Например, для  алгоритма с временной сложностью T(n2) при достаточно больших n можно утверждать, что при увеличении размера задачи (при сортировке - размера массива) в 3 раза время работы алгоритма увеличится в 32=9 раз.

Если операция выполняется  за фиксированное число шагов, не зависящее от размера задачи, то принято писать T(1).

Надо помнить, что перед  одночленом, отражающим сложность, стоит  некоторый коэффициент C. Поэтому  алгоритмы с одинаковой сложностью могут сильно отличаться временем исполнения.

Практически время выполнения алгоритма зависит не только от объема множества данных (размера задачи), но и от их значений, например, время  работы некоторых алгоритмов сортировки значительно сокращается, если первоначально  данные частично упорядочены, тогда  как другие методы оказываются нечувствительными  к этому свойству. Чтобы учитывать  этот факт, полностью сохраняя при  этом возможность анализировать  алгоритмы независимо от данных, различают:

  • максимальную сложность, или сложность наиболее неблагоприятного случая, когда метод работает дольше всего;
  • среднюю сложность - сложность метода в среднем;
  • минимальную сложность - сложность в наиболее благоприятном случае, когда метод справляется быстрее всего.

Алгоритмы, рассматриваемые  здесь, имеют временные сложности T(n2), T(n*log(n)), T(n). Минимальная сложность всякого алгоритма сортировки не может быть меньше T(n). Максимальная сложность метода, оперирующего сравнениями, не может быть меньше T(n*log(n)). Доказательство последнего факта можно найти в многочисленной серьезной теоретической литературе, посвященной проблемам сортировки. Однако есть методы, которые не сравнивают элементы между собой. Мы рассмотрим один из них - сортировку распределением.

Для дальнейшего изложения  нам понадобится следующее тождество:

, n³ m

Доказательство:

Посмотрим, как получаются сложности, с которыми нам придется столкнутся при выполнении лабораторных работ этого учебного пособия. Рассмотрим их в порядке возрастания.

  1. Сложность T(log(n)).

Из всех рассматриваемых  здесь сложностей эта самая малая. Действительно, при достаточно большом n: log(n)<n<n*log(n)<n2.

Эта сложность получается, если на каждом шаге метода выполняется  некоторое постоянное число действий C и задача размерности n сводится к  аналогичной задаче размерностью n/m, где m – целая положительная константа:

 Þ 

Доказательство:

(При доказательстве этой  и нижеследующих формул сложности  используется метод математической  индукции)

    1. F(1)=С*logm(1)=0;
    2. Пусть 

Тогда  , что и требовалось доказать.

Примеры методов с такой  сложностью вы можете найти в лабораторных работах по темам «Методы поиска», «Управление сбалансированными  деревьями», «Управление b-деревьями».

  1. Сложность T(n).

Такая сложность получается в обычных итерационных процессах, когда на каждом шаге метода задача размерности n сводится к задаче размерности n-1, и при этом на каждом шаге выполняется  некоторое постоянное число действий C, не зависящее от n:

 Þ F(n)=C*(n-1)=C*n-C

Доказательство:

    1. F(1)=С*(n-1)=0;
    2. Пусть F(n-1)=C*(n-2)

Тогда F(n)=C+F(n-1)=C+C*(n-2)=C*(1+n-2)=C*(n-1), что и требовалось доказать.

Примером такой сложности  может служит любой цикл, в котором  число итераций прямо зависит  от n. В этой работе такие методы можно  найти при рассмотрении лабораторной работы по теме «Методы поиска». Также  некоторые методы сортировки в лучшем случае (если массив уже отсортирован) выполняют порядка n действий, а сортировка распределением всегда требует порядка n действий.

  1. Сложность T(n*log(n)).

Эта сложность получается, если на каждом шаге метода выполняется  некоторое количество действий C*n, то есть зависящее от размера задачи прямопропорционально, и задача размерности n сводится к задаче размерностью n/m, где m – целая положительная константа:

 Þ F(n)=C’*n*logm(n)

Доказательство:

    1. F(1)=С*logm(1)=0;
    2. Пусть 

Тогда  (0<a <1), что и требовалось доказать.

Примеры алгоритмов с такой  сложностью можно найти в лабораторной работе, посвященной сортировкам.

  1. Сложность T(n2).

Такая сложность получается, когда мы имеем два вложенных  цикла, число итераций которых зависит  от n прямопропорционально. Или другими  словами, на каждом шаге метода задача размерности n сводится к задаче размерности n-1, и при этом выполняется некоторое  количество действий C*n, то есть зависящее  от n прямо пропорционально:

 Þ F(n)=C’*n2+C’’

Доказательство:

, что и требовалось доказать.

Примеры алгоритмов с квадратичной сложностью вы можете найти в лабораторной работе по теме «Методы сортировки». Это все простые и малоэффективные  при больших n методы сортировок (в  том числе получившая широкую  известность благодаря рассмотрению во многих школьных учебниках информатики  пузырьковая сортировка).

При рассмотрении алгоритмов сортировки прежде всего нас будет  интересовать максимальная и средняя  сложности метода, так как именно они, как правило, важны на практике. Максимальная сложность показывает поведение алгоритма в худшем случае, а средняя сложность –  наиболее вероятное поведение при  увеличении размера сортируемого массива. Сводные данные о сложности рассматриваемых  методов можно найти в приложении 2 в конце работы.

Начнем разбор алгоритмов.

2.2.2. Сортировка  подсчетом

Суть метода заключается  в том, что на каждом шаге мы подсчитываем, в какую позицию результирующего  массива надо записать очередной  элемент исходного массива. Выглядит это так:

for i := 1 to N do

begin

  <вычислить_место_k+1_элемента_A[i]

   в_результирующем_массиве_B>

  B[k+1] := A[i];

end;

Слева от B[k+1] должны стоять элементы большие или равные B[k+1]. Значит число k складывается из количества элементов меньших A[i] и, возможно, некоторого числа элементов, равных A[i]. Условимся, что из равных мы будем учитывать  только те элементы, которые в исходном массиве стоят левее A[i]. Теперь вычисление k можно записать следующим образом:

k := 0;

for j := 1 to N do

  if (A[i]>A[j])or((A[i]=A[j])and(i>j)) then

    Inc(k);

Легко видеть, что этот алгоритм всегда имеет сложности T(n2) (два вложенных цикла, зависящих от n линейно) и O(n) (результирующий массив).

2.2.3. Сортировка  включением

В этой сортировке массив делится  на 2 части: отсортированную и неотсортированную. На каждом шаге берется очередной  элемент из неотсортированной части  и «включается» в отсортированную  часть массива.

2.2.3.1. Простое включение

Пусть отсортировано начало массива A[1], A[2], ..., A[i-1], а остаток  массива A[i], ...,A[n] содержит неотсортированную  часть. На очередном шаге будем включать элемент A[i] в отсортированную часть, ставя его на соответствующее  место. При этом придется сдвинуть часть  элементов, больших A[i], (если таковые  есть) на одну позицию правее, чтобы  освободить место для элемента A[i]. Но при сдвиге будет потеряно само значение A[i], поскольку в эту позицию  запишется первый (самый правый - с самым большим индексом) сдвигаемый элемент. Поэтому прежде чем производить  сдвиг элементов необходимо сохранить  значение A[i] в промежуточной переменной.

Так как массив из одного элемента можно считать отсортированным, начнем с i=2.

Программа будет выглядеть  так:

for i := 2 to n do

begin

  Tmp := A[i];

  j := i-1;

  while (A[j]>Tmp)and(j>1) do

  begin

    A[j+1] := A[j];

    Dec(j)

  end;

  A[j+1] := Tmp;

end;

Этот алгоритм также имеет  максимальную и среднюю сложности T(n2), но в случае исходно отсортированного массива внутренний цикл не будет выполняться ни разу, поэтому метод имеет Tmin(n). Можно заметить, что метод использует любой частичный порядок, и чем в большей степени массив исходно упорядочен, тем быстрее он закончит работу. В отличие от предыдущего метода, этот не требует дополнительной памяти.

2.2.3.2. Метод Шелла

Метод Шелла является усовершенствованием простого включения, которое основано на том, что включение использует любой частичный порядок. Но недостатком простого включения является то, что во внутреннем цикле элемент A[i] фактически сдвигается на одну позицию. И так до тех пор, пока он не достигнет своего места в отсортированной части. (На самом деле мы избавлялись от сдвига A[i], сохраняя его в промежуточной переменной, но сути метода это не изменяло, так как передвигалось место, оставленное под сохраненное значение). Метод Шелла позволяет преодолеть это ограничение следующим интересным способом.

Вместо включения A[i] в  подмассив предшествующих ему элементов, его включают в подсписок, содержащий элементы A[i-h], A[i-2h], A[i-3h] и так далее, где h - положительная константа. Таким  образом формируется массив, в  котором «h- серии» элементов, отстоящих  друг от друга на h, сортируются отдельно.

Информация о работе Методы сортировки