Sortuj Shell - Shellsort

Powłoka
Wizualizacja Shellsort krok po kroku
Shellsort z przerwami 23, 10, 4, 1 w akcji
Klasa Algorytm sortowania
Struktura danych Szyk
Wydajność w najgorszym przypadku O( n 2 ) (najgorszy znany najgorszy ciąg przerw)
O( n log 2 n ) (najlepiej znany najgorszy ciąg przerw)
Wydajność w najlepszym przypadku O( n log n ) (większość ciągów przerw)
O( n log 2 n ) (najlepiej znany ciąg przerw w najgorszym przypadku)
Średnia wydajność zależy od sekwencji przerw
Najgorsza złożoność przestrzeni О( n ) ogółem, O(1) pomocniczy
Kroki Shellsorta.
Zamiana par elementów w kolejnych krokach Shellsort z przerwami 5, 3, 1

Shellsort , znany również jako Shell sort lub Shell's method , jest sortowaniem porównującym w miejscu . Może to być postrzegane jako albo uogólnienia sortując wymiany ( pęcherzyka rodzaju ) lub sortowanie według wkładania ( wkładania rodzaju ). Metoda zaczyna się od sortowania par elementów daleko od siebie, a następnie stopniowego zmniejszania odstępu między porównywanymi elementami. Rozpoczynając od oddalonych od siebie elementów, może przesunąć na miejsce niektóre nierówne elementy szybciej niż zwykła wymiana najbliższego sąsiada. Donald Shell opublikował pierwszą wersję tego rodzaju w 1959. Czas działania Shellsort jest silnie zależny od używanej sekwencji przerw. Dla wielu praktycznych wariantów ustalenie ich złożoności czasowej pozostaje otwartym problemem .

Opis

Shellsort to optymalizacja sortowania przez wstawianie, która umożliwia wymianę elementów, które są daleko od siebie. Pomysł polega na ułożeniu listy elementów w taki sposób, aby począwszy od dowolnego miejsca, pobranie każdego h elementu dawało posortowaną listę. Mówi się, że taka lista jest posortowana h . Może być również traktowane jako h przeplatane listami, indywidualnie sortowane. Rozpoczęcie od dużych wartości h umożliwia elementom przemieszczanie się na duże odległości w oryginalnej liście, szybko redukując duże ilości nieporządku i pozostawiając mniej pracy na wykonanie mniejszych kroków sortowania h . Jeśli lista jest następnie posortowana według k dla jakiejś mniejszej liczby całkowitej k , to lista pozostaje posortowana według h . Podążając za tym pomysłem dla malejącej sekwencji wartości h kończących się na 1, gwarantujemy pozostawienie na końcu posortowanej listy.

W uproszczeniu oznacza to, że jeśli mamy tablicę 1024 liczb, nasza pierwsza przerwa ( h ) może wynosić 512. Następnie przeglądamy listę porównując każdy element z pierwszej połowy z elementem w drugiej połowie. Nasza druga przerwa ( k ) wynosi 256, co powoduje rozbicie tablicy na cztery sekcje (zaczynając od 0,256,512,768) i upewniamy się, że pierwsze elementy w każdej sekcji są posortowane względem siebie, a następnie drugi element w każdej sekcji i tak dalej . W praktyce sekwencja przerw może być dowolna, ale ostatnią przerwą jest zawsze 1, aby zakończyć sortowanie (efektywnie kończąc zwykłym sortowaniem przez wstawianie).

Przykładowy przebieg Shellsort z przerwami 5, 3 i 1 pokazano poniżej.

1 2 3 4 5 6 7 8 9 10 11 12
Dane wejściowe 62 83 18 53 07 17 95 86 47 69 25 28
Po 5-sortowaniu 17 28 18 47 07 25 83 86 53 69 62 95
Po 3-sortowaniu 17 07 18 47 28 25 69 62 53 83 86 95
Po 1-sortowaniu 07 17 18 25 28 47 53 62 69 83 86 95

Pierwszy przebieg, 5-sorting, wykonuje sortowanie przez wstawianie na pięciu oddzielnych podtablicach ( a 1 , a 6 , a 11 ), ( a 2 , a 7 , a 12 ), ( a 3 , a 8 ), ( a 4 , a 9 ), ( 5 , 10 ). Na przykład zmienia podtablicę ( a 1 , a 6 , a 11 ) z (62, 17, 25) na (17, 25, 62). Kolejny przebieg, 3-sorting, wykonuje sortowanie przez wstawianie na trzech podtablicach ( a 1 , a 4 , a 7 , a 10 ), ( a 2 , a 5 , a 8 , a 11 ), ( a 3 , a 6 , 9 , 12 ). Ostatni przebieg, 1-sortowanie, jest zwykłym sortowaniem całej tablicy przez wstawianie ( a 1 ,..., a 12 ).

Jak ilustruje przykład, podtablice, na których operuje Shellsort, są początkowo krótkie; później są dłuższe, ale prawie uporządkowane. W obu przypadkach sortowanie przez wstawianie działa sprawnie.

Shellsort nie jest stabilny : może zmienić względną kolejność elementów o równych wartościach. Jest to adaptacyjny algorytm sortowania, który działa szybciej, gdy dane wejściowe są częściowo posortowane.

Pseudo kod

Używając sekwencji luk Marcina Ciury, z wewnętrznym sortowaniem przez wstawianie.

# Sort an array a[0...n-1].
gaps = [701, 301, 132, 57, 23, 10, 4, 1]  // Ciura gap sequence

# Start with the largest gap and work down to a gap of 1
foreach (gap in gaps)
{
    # Do a gapped insertion sort for this gap size.
    # The first gap elements a[0..gap-1] are already in gapped order
    # keep adding one more element until the entire array is gap sorted
    for (i = gap; i < n; i += 1)
    {
        # add a[i] to the elements that have been gap sorted
        # save a[i] in temp and make a hole at position i
        temp = a[i]
        # shift earlier gap-sorted elements up until the correct location for a[i] is found
        for (j = i; j >= gap and a[j - gap] > temp; j -= gap)
        {
            a[j] = a[j - gap]
        }
        # put temp (the original a[i]) in its correct location
        a[j] = temp
    }
}

Sekwencje przerw

Trudne jest podjęcie decyzji, której sekwencji odstępów użyć. Każda sekwencja przerw zawierająca 1 daje poprawne sortowanie (ponieważ ostatnie przejście jest zwykłym sortowaniem przez wstawianie); jednak właściwości tak otrzymanych wersji Shellsort mogą być bardzo różne. Zbyt mało odstępów spowalnia przejazdy, a zbyt wiele odstępów powoduje wzrost.

Poniższa tabela porównuje większość proponowanych sekwencji przerw opublikowanych do tej pory. Niektóre z nich mają malejące elementy, które zależą od rozmiaru posortowanej tablicy ( N ). Inni są rosnącymi ciągami nieskończonymi, których elementy mniejsze niż N powinny być używane w odwrotnej kolejności.

OEIS Termin ogólny ( k ≥ 1) Szczeliny betonowe
Złożoność czasowa w najgorszym przypadku
Autor i rok publikacji
[np. gdy N = 2 p ] Powłoka , 1959
Frank i Łazarz, 1960
A000225 Hibbard , 1963
A083318 , poprzedzony 1 Papernov i Stasevich, 1965
A003586 Kolejne liczby postaci ( liczby 3-gładkie ) Pratt , 1971
A003462 , nie większy niż Knuth , 1973, na podstawie Pratta , 1971
A036569 Incerpi i Sedgewick , 1985, Knuth
A036562 , poprzedzony 1 Sedgewick, 1982
A033622 Sedgewick, 1986
Nieznany Gonnet i Baeza-Yates , 1991
A108870 Nieznany Tokuda, 1992
A102549 Nieznane (pochodzące eksperymentalnie) Nieznany Ciura, 2001

Gdy binarna reprezentacja N zawiera wiele kolejnych zer, sortowanie Shell przy użyciu oryginalnej sekwencji odstępów powłoki wykonuje porównania Θ ( N 2 ) w najgorszym przypadku. Na przykład ten przypadek występuje dla N równego potędze dwójki, gdy elementy większe i mniejsze od mediany zajmują odpowiednio pozycje parzyste i nieparzyste, ponieważ są porównywane tylko w ostatnim przebiegu.

Chociaż ma wyższą złożoność niż O ( N  log  N ), która jest optymalna dla sortowania porównawczego, wersja Pratta nadaje się do sieci sortujących i ma taką samą asymptotyczną złożoność bramki jak sorter bitonic Batchera .

Gonnet i Baeza-Yates zaobserwowali, że Shellsort dokonuje średnio najmniej porównań, gdy stosunki kolejnych przerw są w przybliżeniu równe 2,2. Dlatego ich ciąg o przeliczeniu 2,2 i ciąg Tokudy o przeliczeniu 2,25 okazują się skuteczne. Nie wiadomo jednak, dlaczego tak się dzieje. Sedgewick zaleca stosowanie przerw, które mają niskie największe wspólne dzielniki lub są parami względnie pierwsze .

Jeśli chodzi o średnią liczbę porównań, sekwencja Ciury ma najbardziej znaną wydajność; odstępy od 701 nie zostały określone, ale sekwencja może być dalej rozszerzana zgodnie z formułą rekurencyjną .

Do praktycznych zastosowań można polecić ciąg Tokudy, określony prostym wzorem , gdzie , .

Jeśli maksymalny rozmiar danych wejściowych jest mały, co może się zdarzyć, gdy Shellsort jest używany na małych podtablicach przez inny rekurencyjny algorytm sortowania, taki jak quicksort lub merge sort , wówczas możliwe jest zestawienie optymalnej sekwencji dla każdego rozmiaru wejściowego.

Złożoność obliczeniowa

Posiada następującą właściwość: po h 2 -sorting dowolnego H 1 -sorted tablicy, Tablica pozostaje h 1 -sorted. Każda tablica h 1 -sortowana i h 2 -sortowana jest również sortowana ( a 1 h 1 + a 2 h 2 ) dla dowolnych nieujemnych liczb całkowitych a 1 i a 2 . Najgorszy przypadek złożoności Shellsort jest zatem związany z problemem Frobeniusa : dla danych liczb całkowitych h 1 ,..., h n przy gcd = 1, liczba Frobeniusa g ( h 1 ,..., h n ) jest największa całkowita, która nie może być reprezentowane na 1 godzinę 1 + ... + n h, n z nieujemną liczbę całkowitą o 1 , ..., a n . Używając znanych wzorów na liczby Frobeniusa, możemy określić najgorszy przypadek złożoności Shellsort dla kilku klas sekwencji przerw. Sprawdzone wyniki przedstawiono w powyższej tabeli.

W odniesieniu do średniej liczby operacji żaden ze sprawdzonych wyników nie dotyczy praktycznej sekwencji przerw. Dla przerw, które są potęgami dwójki, Espelid obliczył tę średnią jako . Knuth określił średnią złożoność sortowania N- elementowej tablicy z dwiema przerwami ( h , 1) jako . Wynika z tego, że dwuprzebiegowe sortowanie Shell z h = Θ( N 1/3 ) dokonuje średnio O ( N 5/3 ) porównań/inwersji/czasu działania. Yao znalazł średnią złożoność sortowania Shellsort z trzema przebiegami. Jego wynik został doprecyzowany przez Jansona i Knutha: średnia liczba porównań/inwersji/czasu działania dokonanych podczas sortowania Shell z trzema przerwami ( ch , cg , 1), gdzie h i g są względnie pierwsze, jest w pierwszym przebiegu, w drugim i w trzecim przejściu. ψ ( h , g ) w ostatnim wzorze jest skomplikowaną funkcją asymptotycznie równą . W szczególności, gdy h = ( N 7/15 ) i g = Θ( N 1/5 ), średni czas sortowania wynosi O ( N 23/15 ).

Na podstawie eksperymentów przypuszcza się, że sortowanie Shellsort z sekwencją przerw Hibbarda przebiega w średnim czasie O ( N 5/4 ) i że sekwencja Gonneta i Baeza-Yatesa wymaga średnio 0,41 N  ln  N  (ln ln  N  + 1/6 ) element się porusza. Przybliżenia średniej liczby operacji, które poprzednio były wykonywane dla innych sekwencji, kończą się niepowodzeniem, gdy posortowane tablice zawierają miliony elementów.

Poniższy wykres przedstawia średnią liczbę porównań pierwiastków w różnych wariantach Shellsort podzieloną przez teoretyczną dolną granicę tj. log 2 N !, gdzie sekwencja 1, 4, 10, 23, 57, 132, 301, 701 została rozszerzona według wzoru .

Powłoka sortuje średnią liczbę porównań (angielski).svg

Stosując teorię złożoności Kołmogorowa , Jiang, Li i Vitányi wykazali następujące dolne ograniczenie dla rzędu średniej liczby operacji/czasu działania w p-przebiegowym sortowaniu powłokowym: Ω( pN 1+1/ p ) gdy p  ≤ log 2 N i Ω( pN ) gdy p  > log 2 N . Dlatego Shellsort ma szanse na działanie w średnim czasie, który asymptotycznie rośnie jak N log N tylko wtedy, gdy używa się sekwencji przerw, których liczba przerw rośnie proporcjonalnie do logarytmu rozmiaru tablicy. Nie wiadomo jednak, czy sortowanie Shell może osiągnąć ten asymptotyczny porządek złożoności średniej wielkości, który jest optymalny dla sortowań porównawczych. Dolna granica została poprawiona przez Vitányi dla każdej liczby podań na którym . Wynik ten implikuje na przykład dolną granicę Jiang-Li- Vitányi dla wszystkich sekwencji inkrementacji -pass i poprawia tę dolną granicę dla poszczególnych sekwencji inkrementacji. W rzeczywistości wszystkie granice (dolne i górne) obecnie znane dla przeciętnego przypadku są dokładnie dopasowane przez tę dolną granicę. Na przykład daje to nowy wynik, że górna granica Jansona-Knutha jest dopasowywana przez wynikową dolną granicę dla używanej sekwencji inkrementacji, pokazując, że trzyprzebiegowe Shellsort dla tej sekwencji inkrementacji używa porównań/inwersji/czasu działania. Formuła pozwala nam szukać sekwencji przyrostowych, które dają dolne granice, które są nieznane; na przykład sekwencja inkrementacji dla czterech przejść, która ma dolne ograniczenie większe niż dla sekwencji inkrementacji . Dolna granica staje się

Najgorsza złożoność jakiejkolwiek wersji Shellsort jest wyższego rzędu: Plaxton, Poonen i Suel pokazali, że rośnie co najmniej tak szybko, jak .

Aplikacje

Shellsort wykonuje więcej operacji i ma wyższy współczynnik braku pamięci podręcznej niż quicksort . Jednakże, ponieważ może być zaimplementowana przy użyciu niewielkiej ilości kodu i nie używa stosu wywołań , niektóre implementacje funkcji qsort w standardowej bibliotece C przeznaczonej dla systemów wbudowanych używają jej zamiast quicksort. Shellsort jest używany na przykład w bibliotece uClibc . Z podobnych powodów w przeszłości Shellsort był używany w jądrze Linuksa .

Shellsort może również służyć jako podalgorytm sortowania introspektywnego , do sortowania krótkich podtablic i zapobiegania spowolnieniu, gdy głębokość rekurencji przekracza określony limit. Ta zasada jest stosowana na przykład w kompresorze bzip2 .

Zobacz też

Bibliografia

Bibliografia

Zewnętrzne linki