Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Сортировка бинарными вставками

Поиск

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

Пусть, к примеру, нужно найти место для элемента 7 в таком массиве:

[2 4 6 8 10 12 14 16 18]

Найдем средний элемент этой последовательности (10) и сравним с ним семерку. После этого все, что больше 10 (да и саму десятку тоже), можно смело исключить из дальнейшего рассмотрения:

[2 4 6 8] 10 12 14 16 18

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

nomer_sred:= (nomer_lev + nomer_prav)div 2

Итак, отсечем половину последовательности:

2 4 [6 8] 10 12 14 16 18

И снова:

2 4 6 [8] 10 12 14 16 182 4 6][8 10 12 14 16 18

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

[2 4 6 8] 10 12 14 16 182 4 [6 8] 10 12 14 16 182 4 6 [8] 10 12 14 16 182 4 6 8][10 12 14 16 18

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

Будет ли такой алгоритм универсальным? Давайте проверим, что же произойдет, если мы станем искать позицию не для семерки или девятки, а для единицы:

[2 4 6 8] 10 12 14 16 18[2] 4 6 8 10 12 14 16 18][2 4 6 8 10 12 14 16 18

Как видим, правая граница становится неопределенной - выходит за пределы массива. Будет ли этот факт иметь какие-либо неприятные последствия? Очевидно, нет, поскольку нас интересует не правая, а левая граница.

"А что будет, если мы захотим добавить 21?" - спросит особо въедливый читатель. Проверим это:

2 4 6 8 10 [12 14 16 18]2 4 6 8 10 12 14 [16 18]2 4 6 8 10 12 14 16 [18]2 4 6 8 10 12 14 16 18][

Кажется, будто все плохо: левая граница вышла за пределы массива; непонятно, что нужно сдвигать...

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

Реализация алгоритма БинВст

for i:= 2 to n do if a[i-1]>a[i] then begin x:= a[i]; left:= 1; right:= n-1; repeat sred:= (left+right) div 2; if a[sred]<x then left:= sred+1 else right:= sred-1; until left>right; for j:= i-1 downto left do a[j+1]:= a[j]; a[left]:= x; end;

Эффективность алгоритма БинВст

Теперь на каждом шаге выполняется не N, а log N проверок5, что уже значительно лучше (для примера, сравните 1000 и 10 = log 1024). Следовательно, всего будет совершено N*log N сравнений. Впрочем, улучшение это не слишком значительное, ведь по количеству пересылок наш алгоритм по-прежнему имеет сложность "порядка N2".

Сортировка простым выбором

Попробуем теперь сократить количество пересылок элементов.

Алгоритм ПрВыб

На каждом шаге (всего их будет ровно N-1) будем производить такие действия:

1. найдем минимум среди всех еще не упорядоченных элементов;

2. поменяем его местами с первым "по очереди" не отсортированным элементом. Мы надеемся, что читателям очевидно, почему к концу работы этого алгоритма последний (N-й) элемент массива автоматически окажется максимальным.

Реализация ПрВыб

for i:= 1 to n-1 do begin min_ind:= i; for j:= i+1 to n do if a[j]<=a[min_ind] {***} then min_ind:= j; if min_ind<>i then begin x:= a[i]; a[i]:= a[min_ind]; a[min_ind]:= x; end; end;

Эффективность алгоритма ПрВыб

В лучшем случае (если исходная последовательность уже упорядочена), алгоритм ПрВыб произведет (N-1)*(N+2)/2 сравнений и 0 пересылок данных. В остальных же случаях количество сравнений останется прежним, а вот количество пересылок элементов массива будет равным 3*(N-1).

Таким образом, алгоритм ПрВыб имеет квадратичную сложность (~N2) по сравнениям и линейную (~N) - по пересылкам.

Замечание. Если перед вами поставлена задача отсортировать строки двумерного массива (размерности NxN) по значениям его первого столбца, то сложность алгоритма ПрВыб, модифицированного для решения этой задачи, будет квадратичной (N2 сравнений и N2 пересылок), а алгоритма БинВст - кубической (N*log N сравнений и N3 пересылок). Комментарии, как говорится, излишни.

Пример сортировки

Предположим, что нужно отсортировать тот же набор чисел, при помощи которого мы иллюстрировали метод сортировки простыми вставками:

5 3 4 3 6 2 1

Теперь мы будем придерживаться алгоритма ПрВыб (подчеркнута несортированная часть массива, а квадратиком выделен ее минимальный элемент):

1 шаг: 53436212 шаг: 1343625 3 шаг: 1243635 {***}6 4 шаг: 1233645{ничего не делаем}5 шаг: 12336456 шаг: 1233465результат: 1233456

Сортировка простыми обменами

Рассмотренными сортировками, конечно же, не исчерпываются все возможные методы упорядочения массивов.

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

Тем же, кто все-таки желает ознакомиться с обменными сортировками, а также с подробными данными по сравнению различных сортировок, мы рекомендуем труды Д. Кнута7 или Н. Вирта8.

Улучшенные сортировки

В отличие от простых сортировок, имеющих сложность ~N2, к улучшенным сортировкам относятся алгоритмы с общей сложностью ~N*logN.

Необходимо, однако, отметить, что на небольших наборах сортируемых данных (N<100) эффективность быстрых сортировок не столь очевидна: выигрыш становится заметным только при больших N. Следовательно, если необходимо отсортировать маленький набор данных, то выгоднее взять одну из простых сортировок.

Сортировка Шелла

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

Алгоритм УлШелл

На каждом шаге (пусть переменная t хранит номер этого шага) нужно произвести следующие действия:

1. вычленить все подпоследовательности, расстояние между элементами которых составляет kt;

2. каждую из этих подпоследовательностей отсортировать методом ПрВст.

Нахождение убывающей последовательности расстояний kt, kt-1..., k1 составляет главную проблему этого алгоритма. Многочисленные исследования позволили выявить ее обязательные свойства:

· k1 = 1;

· для всех t kt > kt-1;

· желательно также, чтобы все kt не были кратными друг другу (для того, чтобы не повторялась обработка ранее отсортированных элементов).

Дональд Кнут предлагает две "хорошие" последовательности расстояний:

1, 4, 13, 40, 121, _ (kt = 1+3*kt-1)1, 3, 7, 15, 31, _ (kt = 1+2*kt-1 = 2t -1)

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

Как же определить начальное значение для t (а вместе с ним, естественно, и для kt)?

Можно, конечно, шаг за шагом проверять, возможно ли вычленить из сортируемого массива подпоследовательность (хотя бы длины 2) с расстояниями 1, 3, 7, 15 и т.д. между ее элементами. Однако такой способ довольно неэффективен. Мы поступим иначе, ведь у нас есть формула для вычисления kt = 2t-1.

Итак, длина нашего массива (N) должна попадать в такие границы:

kt <= N -1 < kt+1

или, что то же самое,

2t <= N < 2t+1

Прологарифмируем эти неравенства (по основанию 2):

t <= log N < t+1

Таким образом, стало ясно, что t можно вычислить по следующей формуле:

t = trunc(log N))

К сожалению, язык Pascal предоставляет возможность логарифмировать только по основанию е (натуральный логарифм). Поэтому нам придется вспомнить знакомое из курса средней школы правило "превращения" логарифмов:

logmx =logzx/logzm

В нашем случае m = 2, z = e. Таким образом, для начального t получаем:

t:= trunc(ln(N)/ln(2)).

Однако при таком t часть подпоследовательностей будет иметь длину 2, а часть - и вовсе 1. Сортировать такие подпоследовательности незачем, поэтому стоит сразу же отступить еще на 1 шаг:

t:= trunc(ln(N)/ln(2))-1

Расстояние между элементами в любой подпоследовательности вычисляется так:

k:= (1 shl t)-1; {k= 2t-1}

Количество подпоследовательностей будет равно в точности k. В самом деле, каждый из первых k элементов служит началом для очередной подпоследовательности. А дальше, начиная с (k+1)-го, все элементы уже являются членами некоторой, ранее появившейся подпоследовательности, значит, никакая новая подпоследовательность не сможет начаться в середине массива.

Сколько же элементов будет входить в каждую подпоследовательность? Ответ таков: если длину всей сортируемой последовательности (N) можно разделить на шаг k без остатка, тогда все подпоследовательности будут иметь одинаковую длину, а именно:

s:= N div k;

Если же N не делится на шаг k нацело, то первые р подпоследовательностей будут длиннее на 1. Количество таких "удлиненных" подпоследовательностей совпадает с длиной "хвоста" - остатка от деления N на шаг k:

P:= N mod k;

Реализация алгоритма УлШелл

Ради большей наглядности мы пожертвовали эффективностью и воспользовались алгоритмом ПрВст, а не ПрВстБар или БинВст. Дотошному же читателю предоставляется возможность самостоятельно улучшить предлагаемую реализацию:

program shell_sort;const n=18; a:array[1..n] of integer =(18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1);var ii,m,x,s,p,t,k,r,i,j: integer;begin t:= trunc(ln(n)/ln(2)); repeat t:= t-1; k:= (1 shl t)-1; p:= n mod k; s:= n div k; if p=0 then p:= k else s:= s+1; writeln(k,'-сортировка'); for i:= 1 to k do {берем и длинные, и короткие подпоследовательности} begin if i= p+1 then s:= s-1; (для коротких - уменьшаем длину} for j:= 1 to s-1 do {метод ПрВст с шагом k} if a[i+(j-1)*k]>a[i+j*k] then begin x:= a[i+j*k]; m:= i+(j-1)*k; while (m>0) and (a[m]>x) do begin a[m+k]:= a[m]; m:= m-k; end; a[m+k]:= x; end; for ii:= 1 to n do write(a[ii],' '); writeln; end; until k=1;end.

Результат работы

Сортировки

4 17 16 15 14 13 12 11 10 9 8 7 6 5 18 3 2 1 4 3 16 15 14 13 12 11 10 9 8 7 6 5 18 17 2 1 4 3 2 15 14 13 12 11 10 9 8 7 6 5 18 17 16 1 4 3 2 1 14 13 12 11 10 9 8 7 6 5 18 17 16 15 4 3 2 1 7 13 12 11 10 9 8 14 6 5 18 17 16 15 4 3 2 1 7 6 12 11 10 9 8 14 13 5 18 17 16 15 4 3 2 1 7 6 5 11 10 9 8 14 13 12 18 17 16 15

Сортировки

1 3 2 4 7 6 5 11 10 9 8 14 13 12 18 17 16 15 1 3 2 4 7 6 5 8 10 9 11 14 13 12 18 17 16 15 1 3 2 4 7 6 5 8 10 9 11 14 13 12 15 17 16 18

Сортировка

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18


Поделиться:


Последнее изменение этой страницы: 2016-07-16; просмотров: 267; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.144.25.248 (0.011 с.)