Shell sort

Shell sort (Shellovo řazení, řazení se snižujícím se přírůstkem) je kvadratický řadící algoritmus podobný insertion sortu. Ačkoliv má také složitost Ο(n^{2}), tak je z algoritmů tohoto typu nejvýkonnější.

Princip

Běžný insertion sort si uchovává seznam již seřazených prvků. V každém svém kroku zařadí do seznamu prvek, který s tímto seznamem přímo sousedí. Opakováním tohoto postupu od triviálního případu (seznam čítající pouze první prvek) algoritmus seřadí pole za n iterací.

Shell sort funguje obdobně. Základním rozdílem však je, že Shell sort využívá tzv. snižující se přírůstek. To znamená, že algoritmus neřadí prvky, které jsou přímo vedle sebe, ale prvky, mezi nimiž je určitá mezera (tj. první a pátý, pátý a devátý, druhý a šestý...). V každém kroku je pak mezera mezi prvky zmenšena. V okamžiku, kdy se velikost mezery sníží na 1, dojde k řazení sousedních prvků – algoritmus degeneruje na běžný insertion sort. Výhodou tohoto poněkud komplikovaného přístupu je, že jsou prvky vysokých a nízkých hodnot velmi rychle přemístěny na odpovídající stranu pole. Poslední iterace algoritmu (insertion sort) pak již přesouvá naprosté minimum prvků.

Ideální mezera

Základním problémem je ovšem volba ideální vzdálenosti pro porovnávání jednotlivých prvků. Donald Shell (autor algoritmu) původně navrhoval, aby se začínalo na n/2 (n je velikost pole) a vzdálenost se vždy půlila. Tento přístup má ovšem tu nevýhodu, že se prvky na sudých a lichých místech porovnávají až v posledním kroku algoritmu. Dalšími pokusy byly například posloupnosti 2^k - 1 (Hibbard) se složitostí Ο(n^{3/2}) nebo 9 \cdot 4^{i} \, - \, 9 \cdot 2^{i} (Sedgewick) se složitostí Ο(n^{4/3}), případně Fibonacciho posloupnost umocněná na dvojnásobek zlatého řezu.

Nejlepší výsledky ovšem podává posloupnost 1, 4, 10, 23, 57, 132, 301, 701, 1750, dále mezera \cdot 2.2, jejímž autorem je Marcin Ciura.

Shell sort (Licence: Public domain)
Shell sort - vizualizace

Kód

    
    /**
     * Shell sort - Shellovo razeni - razeni se snizujicim se prirustkem (od nejvyssiho)
     * @param array pole k serazeni
     * @return serazene pole
     */
    public static int[] shellSort(int[] array) {
        int gap = array.length / 2;
        while (gap > 0) { //dokud mame co porovnavat
            for (int i = 0; i < array.length - gap; i++) { //upraveny insertion sort
                int j = i + gap;
                int tmp = array[j];
                while (j >= gap && tmp > array[j - gap]) {
                    array[j] = array[j - gap];
                    j -= gap;
                }
                array[j] = tmp;
            }
            if (gap == 2) { //zmena velikosti mezery
                gap = 1;
            } else {
                gap /= 2.2;
            }
        }
        return array;
    }
/**
 * Shell sort - Shellovo razeni - razeni se snizujicim se prirustkem (od nejvyssiho)
 * @param array pole k serazeni
 * @param size velikost pole
 * @return serazene pole
 */
int * shellSort(int * array, int size) {
    int gap = size / 2;
    while (gap > 0) { //dokud mame co porovnavat
        for (int i = 0; i < size - gap; i++) { //upraveny insertion sort
            int j = i + gap;
            int tmp = array[j];
            while (j >= gap && tmp > array[j - gap]) {
                array[j] = array[j - gap];
                j -= gap;
            }
            array[j] = tmp;
        }
        if (gap == 2) { //zmena velikosti mezery
            gap = 1;
        } else {
            gap /= 2.2;
        }
    }
    return array;
}
        /**
         * Shell sort - Shellovo razeni - razeni se snizujicim se prirustkem (od nejvyssiho)
         * @param array pole k serazeni
         * @return serazene pole
         */
        public static int[] ShellSort(int[] array)
        {
            int gap = array.Length / 2;
            while (gap > 0) //dokud mame co porovnavat
            { 
                for (int i = 0; i < array.Length - gap; i++) //upraveny insertion sort
                { 
                    int j = i + gap;
                    int tmp = array[j];
                    while (j >= gap && tmp > array[j - gap])
                    {
                        array[j] = array[j - gap];
                        j -= gap;
                    }
                    array[j] = tmp;
                }
                if (gap == 2) //zmena velikosti mezery
                { 
                    gap = 1;
                }
                else
                {
                    gap = (int)(gap / 2.2);
                }
            }
            return array;
        }
{ zpětná vazba }
Delicious Delicious
Sdílet
Hodnocení (0): 0

Přečtěte si také

Diskuse





Pavel Mička15.12.2010
Ten patrně neexistuje...zde se bavíme o empirických skutečnostech...co jsem se před týdnem díval do toho článku od Ciury, tak tam psal (pokud si dobře pamatuji), že některé skutečnosti napovídají tomu, že by se mohlo jednat o optimální mezery...
mkmkmk15.12.2010
zajimave ze my mame ve skriptech a nejen ve skriptech je uvedeno ze neexistuje teoreticky dukaz o nejlepsi posloupnosti velikosti kroku..
Pavel Mička12.12.2010
Přiznám se, že jsem to nepitval až tak do hloubky :-), ale mám za to, že se na to přišlo empiricky...

Podle toho, co jsem teď četl by těch 2.2 mělo být z práce Marka Allena Weisse (který přišel na to, že se to chová pěkněji než 2) a ta řada samotná je z práce Marcina Ciury (tama le ta řada končí 1750)... čili jde patrně o složení dvou výsledků.
Patrik Milat12.12.2010
Překvapila mně rychlost tohoto seřazovacího algoritmu. Při vygenerování 999 999 náhodných čisel v rozsahu 1-1000 dosahuje tento algoritmus vždy času do 1 vteřiny, přičemž ostatní seřazovací algoritmy (bubble sort, selection sort do 15-30sekund, ale při 1/10 náhodných čísel). Zajimálo by mně, jak přišli na tu ideální mezeru 2.2, při posloupnosti 1, 4, 10, 23, 57, 132, 301, 701, 1750 mi to nevychází. Je to tím, že dále v posloupnosti jsou menší mezery a k číslu 2.2 se došlo zaokrouhlením?
Mirek27.1.2010
Díky, už to chápu :) a děkuji za odpověď :)
Pavel Mička21.1.2010
Mirek: te mezery, ktera dela z shell sortu shell sort :-))...v tom sortu jde o to, ze napred tridis treba prvky "ob 8 prvku", v dalsim kroku radis prvky "ob 4 prvky", pak "ob 2", "ob 1" a nakonec sousedni (coz by mel byt asi klasicky insertion sort)...to "ob kolik prvku radis" (respektive spis vzdalenost razenych prvku), je ta mezera :-)
Mirek20.1.2010
Zdravím, chtěl bych se zeptat, jak je to se řádkem 19-22. K čemu tato podmínka slouží? vím, že podle komentáře ke změně velikosti mezery.. ale jaké mezery? Předem děkuji za odpověď, Mirek.