Алгоритъмът на Шел (на английски: Shellsort) (нар. още метод на Шел, Шел метод) е алгоритъм за сортиране, за който се взема предвид положението на елементите. Той обобщава други сортиращи методи като метода на мехурчето и метода за сортиране чрез вмъкване, като започва сравнението и разменянето на елементите, с онези, които са отдалечени, преди да се извърши сравнението със съседните. Именно тази характеристика на метода го отличава и го прави по-бърз от предшествениците му. Доналд Шел публикува първата реализация на този метод през 1959 г. Времето за изпълнение на алгоритъма на Шел строго зависи от различната последователност, с която работи.

Пример как алгоритъмът на Шел сортира една редица

Описание редактиране

Алгоритъмът на Шел е обобщение на метода за сортиране чрез вмъкване, като позволява размяна на елементи, които са разделечени. Идеята е да се подреди листът от елементи, като се започне от някъде (обикновено около средата) и се смята, че всеки h-ти елемент дава сортиран лист. Подобен лист се казва, че е h-сортиран. Накрая се получават листове от интервали, всеки от които индивидуално сортиран. Започването с голяма стойност на h, позволява на елементите да се придвижват на големи разстояния и намалява възможността за доста произволни разбърквания. Също така се оставят по-малко на брой операции за малките h-сортирания.

Методът е нестабилен. Той може да смени наредбата на елементи с еднаква стойност. Естественото му поведение е, че работи бързо когато масивът е отчасти сортиран.

Следният пример показва сортиране чрез метода на Шел при разлика съответно 5, 3 и 1:

 
Алгоритъм на Шел цветови алгоритъм барове
входни данни:                  80       72      71      30      64      43      98      41       30       01
след 5-сортирне:               43       72      41      30      01      80      98      71       30       64
след 3-сортиране:              01       30      30      64      41      71      43      72       98       80
след 1-сортиране:              01       30      30      41      43      64      71      72       80       98

Имплементация на метода редактиране

C

 void shellSortPhase(int arr[], int length, int gap)
 {
     int i;
     for (i = gap; i < length; ++i)
     {
         int value = arr[i];
         int j;
         for (j = i – gap; j >= 0 && arr[j] > value; j -= gap)
         {
             arr[j + gap] = arr[j];
         }
         arr[j + gap] = value;
     }
 }

 void shellSort(int arr[], size_t length)
{
     static const int gaps[] = {1, 4, 10, 23, 57, 132, 301, 701};
     int sizeIndex;

     for (sizeIndex = sizeof(gaps)/sizeof(gaps[0]) – 1; sizeIndex >= 0; --sizeIndex)
         shellSortPhase(arr, length, gaps[sizeIndex]);
 }

Java

public static <T extends Comparable<? super T>>
 void shellSort(T[] arr)
 {
    for (int gap = arr.length / 2; gap > 0; gap /= 2)
    {
        for (int i = gap; i < arr.length; i++)
        {
            T val = arr[i];
            int j;
            for (j = i; j >= gap && arr[j - gap].compareTo(val) > 0; j -= gap)
            {
                arr[j] = arr[j - gap];
            }
            arr[j] = val;
        }
    }
}

Гап поредици редактиране

Гап (на английски: gap) или т.нар. дупка, представлява разликата между два елемента.

Въпросът при решаването коя гап поредица да се ползва е труден. Всяка една гап поредица, която съдържа една вече получена разлика, представлява вярно сортирана последователност. Независимо от това, получената по този начин реализация, може да бъде доста трудна за метода на Шел.

Таблицата по-долу сравнява най-често срещаните гап поредици. Някои от тях имат намаляващи елементи, които зависят от размера на сортирания масив (N). Други са безкрайно растящи редици, чиито елементи, по-малки от N, би трябвало да бъдат използвани в обратен ред.

Обичаен период (k ≥ 1) Бетонен гап Сложност в най-
лошия случай
Автор и година на публикацията
      [когато N=2p] Шел, 1959
      Хибърд, 1963
 , префиксно с 1     Папернов & Штасевич, 1965
следващите числа във формулата       Прат, 1971
 , не повече от       Кнут, 1973
 
 
 
 
    Инцепри & Седжвик, 1985
 , префиксно с 1     Седжвик, 1986
    ? Ганет & Баеза-Йейтс, 1991
    ? Токуда, 1992
неизвестен   ? Циура, 2001

Когато двоичното представяне на N съдържа последователни нули, методът използва оригиналната редица на Шел, която прави Θ(N2) сравнения в най-лошия случай.

Най-добрата позната ни гап редица е 1, 4, 10, 23, 57, 132, 301, 701. Оптимални гап редици след 701 остават неизвестни, но добри резултати също могат да бъдат наблюдавани при нарастване на горната редица според следната формула  .

Изчислителна сложност редактиране

След всяко h2 сортиране на някой h1-сортиран масив, масивът остава h1-сортиран. Всеки h1-сортиран и h2-сортиран масив могат също да се представят като (a1h1+a2h2)-сортирани, за всички неотрицателни целочислени a1 и a2. Следователно, сложността в най-лошия случай на метода на Шел е силно свързана с Проблема на Фробениус: за дадени целочислени стойсти h1,…,hn с най-малък общ делител 1, Фробениовото число g(h1,…,h2) е най-голямото цяло число, което не може да бъде представено като a1h1+…+anhn използвайки неотрицателни целочислени a1,…,an. Използвайки познатата формула за Фробениови числа, може да се изчисли сложността в най-лошия случай на метода на Шел. Доказани резултати са показани в по-горната таблица.

Никой от показаните резултати не се интересува от гап поредицата в действителността.

Приложение редактиране

Алгоритъмът на Шел не намира широко разпространение в съвременния свят. Причината е, че изпълнява повече операции от бързото сортиране. Независимо от това, фактът, че не използва стека и за имплементацията му е нужно малко парче код, в някои стандартни библиотеки на C, е използван с предимство пред бързото сортиране. Примерно приложение на метода на Шел е в библиотеката uClibc.

Също така методът на Шел може да бъде използван за сортиране на парчета от масиви при интроспективния метод с цел да се предотврати забиване в дълбока рекурсия превишаваща дадената времева граница.