Shellsort - Shellsort

Shellsort
Podrobná vizualizace Shellsortu
Shellsort s mezerami 23, 10, 4, 1 v akci
Třída Algoritmus řazení
Datová struktura Pole
Nejhorší výkon O ( n 2 ) (nejhorší známá nejhorší mezerová sekvence)
O ( n log 2 n ) (nejznámější nejhorší případová mezerová sekvence)
Nejlepší výkon O ( n log n ) (většina sekvencí mezer)
O ( n log 2 n ) (nejznámější sekvence mezer v nejhorším případě)
Průměrný výkon závisí na mezerové sekvenci
Nejhorší prostorová složitost О ( n ) celkem, O (1) pomocný
Kroky Shellsortu.
Prohození dvojic položek v postupných krocích Shellsortu s mezerami 5, 3, 1

Shellsort , také známý jako Shell sort nebo Shellova metoda , je srovnání na místě . Může být vnímáno buď jako zobecnění řazení podle výměny ( bublinkové řazení ), nebo řazení podle vložení ( vkládací řazení ). Metoda začíná tříděním párů prvků daleko od sebe, poté postupným zmenšováním mezery mezi porovnávanými prvky. Začínáme-li s prvky daleko od sebe, může přesunout některé nemístné prvky do polohy rychleji než jednoduchá výměna nejbližších sousedů. Donald Shell publikoval první verzi tohoto druhu v roce 1959. Doba běhu Shellsortu je silně závislá na sekvenci mezer, kterou používá. U mnoha praktických variant zůstává určení jejich časové náročnosti otevřeným problémem .

Popis

Shellsort je optimalizace typu vkládání, která umožňuje výměnu položek, které jsou daleko od sebe. Cílem je uspořádat seznam prvků tak, aby počínaje kdekoli, každý h th prvek vytvořil seřazený seznam. Takový seznam je prý h -tříděný. Lze jej také považovat za h proložené seznamy, každý jednotlivě seřazený. Počínaje velkými hodnotami h umožňuje prvkům pohybovat se v původním seznamu na dlouhé vzdálenosti, což rychle snižuje velké množství nepořádku a ponechává méně práce pro menší h -sort kroky. Pokud je seznam potom k -seřazen pro nějaké menší celé číslo k , pak seznam zůstane h -sorted. Podle této myšlenky na klesající posloupnost hodnot h končících na 1 je zaručeno, že na konci zůstane seřazený seznam.

Zjednodušeně to znamená, že pokud máme pole 1024 čísel, naše první mezera ( h ) by mohla být 512. Poté procházíme seznamem a porovnáváme každý prvek v první polovině s prvkem ve druhé polovině. Naše druhá mezera ( k ) je 256, což rozděluje pole na čtyři sekce (počínaje 0,256,512,768), a my se ujistíme, že první položky v každé sekci jsou vzájemně seřazeny, pak druhá položka v každé sekci atd. . V praxi může být sekvence mezery cokoli, ale poslední mezera je vždy 1 pro dokončení třídění (efektivní dokončení obyčejným vložením).

Níže je uveden příklad běhu Shellsortu s mezerami 5, 3 a 1.

a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 a 10 a 11 a 12
Vstupní data 62 83 18 53 07 17 95 86 47 69 25 28
Po 5-třídění 17 28 18 47 07 25 83 86 53 69 62 95
Po třídění 17 07 18 47 28 25 69 62 53 83 86 95
Po 1-třídění 07 17 18 25 28 47 53 62 69 83 86 95

První průchod, třídění, provádí třídění vkládání na pět samostatných podoblastí ( a 1 , a 6 , a 11 ), ( a 2 , a 7 , a 12 ), ( a 3 , a 8 ), ( a 4 , a 9 ), ( a 5 , a 10 ). Například změní podoblast ( a 1 , a 6 , a 11 ) z (62, 17, 25) na (17, 25, 62). Další průchod, třídění, provede třídění vložení na tři dílčí pole ( a 1 , a 4 , a 7 , a 10 ), ( a 2 , a 5 , a 8 , a 11 ), ( a 3 , a 6 , a 9 , a 12 ). Poslední průchod, 1-třídění, je obyčejný vkládací druh celého pole ( a 1 , ..., a 12 ).

Jak ukazuje příklad, dílčí pole, na kterých Shellsort pracuje, jsou zpočátku krátká; později jsou delší, ale téměř objednané. V obou případech funguje třídění vkládání efektivně.

Shellsort není stabilní : může změnit relativní pořadí prvků se stejnými hodnotami. Jedná se o algoritmus adaptivního řazení v tom, že se provádí rychleji, když je vstup částečně seřazen.

Pseudo kód

Použití mezerové sekvence Marcina Ciury s vnitřním zařazením.

# 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
    }
}

Sekvence mezer

Otázka rozhodnout, kterou sekvenci mezer použít, je obtížná. Každá mezerová sekvence, která obsahuje 1, poskytuje správné řazení (protože to činí konečný průchod obyčejným vkládacím tříděním); vlastnosti takto získaných verzí Shellsortu se však mohou velmi lišit. Příliš málo mezer zpomaluje přihrávky a příliš mnoho mezer vytváří režii.

Níže uvedená tabulka porovnává většinu dosud navržených sekvencí mezer. Některé z nich mají klesající prvky, které závisí na velikosti tříděného pole ( N ). Jiné zvyšují nekonečné sekvence, jejichž prvky menší než N by měly být použity v opačném pořadí.

OEIS Obecný termín ( k ≥ 1) Betonové mezery
Časová složitost v nejhorším případě
Autor a rok vydání
[např. když N = 2 p ] Shell , 1959
Frank & Lazarus, 1960
A000225 Hibbard , 1963
A083318 s předponou 1 Papernov a Staševič, 1965
A003586 Po sobě jdoucí čísla formuláře ( 3 hladká čísla) Pratt , 1971
A003462 , ne větší než Knuth , 1973, podle Pratt , 1971
A036569 Incerpi & Sedgewick , 1985, Knuth
A036562 s předponou 1 Sedgewick, 1982
A033622 Sedgewick, 1986
Neznámý Gonnet & Baeza-Yates , 1991
A108870 Neznámý Tokuda, 1992
A102549 Neznámý (experimentálně odvozený) Neznámý Ciura, 2001

Když binární reprezentace N obsahuje mnoho po sobě jdoucích nul, Shellsort pomocí Shellovy původní mezerové sekvence provede srovnání Θ ( N 2 ) v nejhorším případě. Například tento případ nastává pro N rovný síle dvou, když prvky větší a menší než medián zaujímají liché a sudé pozice, protože jsou porovnávány pouze v posledním průchodu.

Přestože má Prattova verze vyšší složitost než O ( N  log  N ), která je optimální pro srovnávací druhy, je vhodná k třídění sítí a má stejnou asymptotickou složitost brány jako Batcherův bitonický třídič .

Gonnet a Baeza-Yates zjistili, že Shellsort provádí v průměru nejméně srovnání, když jsou poměry po sobě jdoucích mezer zhruba stejné jako 2,2. Proto se jejich sekvence s poměrem 2,2 a Tokudova sekvence s poměrem 2,25 ukázala jako účinná. Není však známo, proč tomu tak je. Sedgewick doporučuje použít mezery, které mají nízké největší společné dělitele nebo jsou párové coprime .

S ohledem na průměrný počet srovnání má sekvence Ciury nejznámější výkon; mezery ze 701 nebyly stanoveny, ale sekvenci lze dále prodloužit podle rekurzivního vzorce .

Tokuda je sekvence, které jsou definovány pomocí jednoduchého vzorce , kde , , lze doporučit pro praktické aplikace.

Pokud je maximální velikost vstupu malá, což může nastat, pokud je Shellsort používán na malých dílčích polích jiným algoritmem rekurzivního řazení, jako je rychlé řazení nebo sloučení , pak je možné pro každou velikost vstupu vytvořit tabulku s optimální sekvencí.

Výpočetní náročnost

Platí následující vlastnost: po h 2 -třídění jakéhokoli h 1 -tříděného pole, pole zůstane h 1 -tříděno. Každý h 1 -sorted a h 2 -sorted pole je také ( 1 h 1 + 2 H 2 ) -sorted, pro jakékoliv nezáporné celá čísla 1 a 2 . Složitost Shellsortu v nejhorším případě je tedy spojena s Frobeniusovým problémem : pro daná celá čísla h 1 , ..., h n s gcd = 1 je Frobeniusovo číslo g ( h 1 , ..., h n ) největší celé číslo, které nemohou být reprezentován jako v 1 h 1 + ... + n h n s nezáporné celé číslo z 1 , ..., a n . Pomocí známých vzorců pro čísla Frobenius můžeme určit nejhorší složitost Shellsortu pro několik tříd mezerových sekvencí. Osvědčené výsledky jsou uvedeny ve výše uvedené tabulce.

Pokud jde o průměrný počet operací, žádný z prokázaných výsledků se netýká praktické mezery. Pro mezery, které jsou mocninami dvou, vypočítal Espelid tento průměr jako . Knuth určil průměrnou složitost třídění pole N -prvku se dvěma mezerami ( h , 1) na . Z toho vyplývá, že dvouprůchodový Shellsort s h = Θ ( N 1/3 ) činí v průměru O ( N 5/3 ) srovnání/inverze/doba běhu. Yao zjistil průměrnou složitost tříprůchodového Shellsortu. Jeho výsledek zpřesnili Janson a Knuth: průměrný počet srovnání/inverzí/doby běhu provedeného během Shellsortu se třemi mezerami ( ch , cg , 1), kde h a g jsou coprime, je v prvním průchodu, ve druhém přihrávka a ve třetím průchodu. ψ ( h , g ) v posledním vzorci je komplikovaná funkce asymptoticky rovná . Zejména když h = Θ ( N 7/15 ) a g = Θ ( N 1/5 ), průměrná doba třídění je O ( N 23/15 ).

Na základě experimentů, to je se domníval, že ShellSort s Hibbard je mezera sekvence běží v O ( N 5/4 ) průměrná doba, a že Gonnet a Baeza-Yates Sekvence vyžaduje v průměru o 0,41 N  ln  N  (ln ln  N  + 1/6 ) prvek se pohybuje. Přibližování průměrného počtu operací dříve předložených pro jiné sekvence selže, když seřazená pole obsahují miliony prvků.

Níže uvedený graf ukazuje průměrný počet porovnání prvků v různých variantách Shellsortu dělený teoretickou dolní mezí, tj. Log 2 N !, Kde byla rozšířena sekvence 1, 4, 10, 23, 57, 132, 301, 701 podle vzorce .

Shell sort průměrný počet srovnání (anglicky). Svg

Použitím teorie Kolmogorovovy složitosti prokázali Jiang, Li a Vitányi následující dolní mez pro pořadí průměrného počtu operací/ doby běhu v p -pass Shellsort: Ω ( pN 1+1/ p ), když p  ≤ log 2 N a Ω ( pN ), když p  > log 2 N . Shellsort má proto vyhlídky na běh v průměrném čase, který asymptoticky roste jako N log N pouze při použití sekvencí mezer, jejichž počet mezer roste úměrně k logaritmu velikosti pole. Není však známo, zda Shellsort dokáže dosáhnout tohoto asymptotického řádu složitosti průměrných případů, které je optimální pro srovnávací druhy. Dolní hranice se zlepšila Vitányi pro každé počtu průchodů do kde . Tento výsledek implikuje například dolní hranici Jiang-Li-Vitányiho pro all-pass přírůstkové sekvence a zlepšuje tuto spodní hranici pro konkrétní přírůstkové sekvence. Ve skutečnosti jsou všechny meze (dolní a horní), které jsou v současné době známé pro průměrný případ, přesně shodné s touto dolní hranicí. Například to dává nový výsledek, že horní hranice Janson-Knuth je shodná s výslednou dolní mezí pro použitou přírůstkovou sekvenci, což ukazuje, že tříprůchodový Shellsort pro tuto přírůstkovou sekvenci používá srovnání/inverze/doba běhu. Vzorec nám umožňuje hledat přírůstkové sekvence, které dávají dolní hranice, které nejsou známy; například přírůstková sekvence pro čtyři průchody, která má spodní hranici větší než pro přírůstkovou sekvenci . Dolní mez se stává

Složitost nejhoršího případu jakékoli verze Shellsortu je vyššího řádu: Plaxton, Poonen a Suel ukázali, že roste minimálně stejně rychle jako .

Aplikace

Shellsort provádí více operací a má vyšší poměr chyb mezipaměti než quicksort . Jelikož jej však lze implementovat pomocí malého kódu a nepoužívá zásobník volání , některé implementace funkce qsort ve standardní knihovně C cílené na vestavěné systémy ji používají místo quicksortu. Shellsort se například používá v knihovně uClibc . Z podobných důvodů byl v minulosti v jádře Linuxu používán Shellsort .

Shellsort může také sloužit jako subalgoritmus introspektivního třídění , k třídění krátkých dílčích polí a k prevenci zpomalení, když hloubka rekurze překročí danou mez. Tento princip se používá například v kompresoru bzip2 .

Viz také

Reference

Bibliografie

externí odkazy