Cache-lhostejná distribuce druh - Cache-oblivious distribution sort

Mezipaměť zapomíná rozdělení distribuce je na základě srovnání třídicí algoritmus. Je to podobné jako quicksort, ale je to algoritmus bez paměti cache, určené pro nastavení, kde je počet prvků k třídění příliš velký, aby se vešel do a mezipaměti kde se operace provádějí. V model externí paměti, počet přenosů paměti, které potřebuje k provedení položky na stroji s velikostí mezipaměti a délka mezipaměti je , za předpokladu vysoké mezipaměti . Ukázalo se, že tento počet přenosů paměti je asymptoticky optimální pro srovnávací druhy. Tento způsob distribuce také dosahuje asymptoticky optimální běhové složitosti .

Algoritmus

Přehled

Třídění distribuce funguje na souvislou řadu elementy. Chcete-li seřadit prvky, provede následující:

  1. Rozdělte pole na souvislé podskupiny velikostí a rekurzivně seřadit každé dílčí pole.
  2. Distribuujte prvky seřazených dílčích polí do kbelíky každý o velikosti nejvýše tak, že pro každé i od 1 do q-1, každý prvek kbelíku není větší než jakýkoli prvek v Tento distribuční krok je hlavním krokem tohoto algoritmu a je podrobněji popsán níže.
  3. Rekurzivně seřaďte každý segment.
  4. Výstup zřetězení segmentů.


Distribuční krok

Jak již bylo zmíněno v kroku 2 výše, cílem distribučního kroku je distribuovat seřazené dílčí pole do q kbelíků Algoritmus distribučního kroku udržuje dva invarianty. První je, že každý segment má maximálně velikost kdykoli a jakýkoli prvek v kbelíku není větší než jakýkoli prvek v kbelíku Druhým je, že každý segment má přidružené pivot, hodnota, která je větší než všechny prvky v segmentu.

Zpočátku algoritmus začíná jedním prázdným kbelíkem s otočným čepem . Když plní kbelíky, vytváří nové kbelíky rozdělením kbelíku na dvě části, když by byl přeplněný (tím, že má alespoň prvky do něj vložené). Rozdělení se provádí provedením lineární časový medián nálezu algoritmus a rozdělení na základě tohoto mediánu. Otočný čep dolního kbelíku bude nastaven na nalezenou střední hodnotu a otočný knoflík vyššího kbelíku bude nastaven na stejný jako kbelík před rozdělením. Na konci distribučního kroku jsou všechny prvky v kbelících a dva invarianty budou stále držet.

K dosažení tohoto cíle bude mít každé dílčí pole a segment k němu přidružený stav. Stav dílčího pole se skládá z indexu další dalšího prvku, který se má načíst z dílčího pole, a číslo segmentu bnum označující, do kterého indexu segmentu má být prvek zkopírován. Podle konvence pokud byly distribuovány všechny prvky v podskupině. (Všimněte si, že když rozdělíme kbelík, musíme vše zvýšit bnum hodnoty všech podskupin, jejichž bnum value is greater than the index of the bucket that is split.) The state of a bucket consists of the value of the bucket's pivot, and the number of elements currently in the bucket.

Zvažte následující základní strategii: iterujte každým dílčím polem a pokuste se kopírovat přes jeho prvek na pozici další. Pokud je prvek menší než čep lopaty bnum, pak jej vložte do tohoto kbelíku, což by mohlo způsobit rozdělení kbelíku. Jinak přírůstek bnum dokud nenajdete kbelík, jehož čep je dostatečně velký. Ačkoli to správně distribuuje všechny prvky, nevykazuje dobrý výkon mezipaměti.

Místo toho se distribuční krok provádí rekurzivním rozdělením a dobýváním. Krok bude proveden jako volání funkce distribuovat, který má tři parametry i, j a m. distribuovat(i, j, m) bude distribuovat prvky z i-tého (i + m-1) -tého dílčího pole do segmentů, počínaje od . Předpokladem je, aby každé dílčí pole r v rozsahu má svoje . Poprava distribuovat(i, j, m) zaručí, že každý . Celý distribuční krok je distribuovat. Pseudokód pro implementaci distribuce je uveden níže:

def distribuovat(i, j, m: int) -> Žádný:    "" "Distribuovat prostřednictvím rekurzivního rozdělení a dobývání." ""    -li m == 1:        copy_elems(i, j)    jiný:        distribuovat(i, j, m / 2)        distribuovat(i + m / 2, j, m / 2)        distribuovat(i, j + m / 2, m / 2)        distribuovat(i + m / 2, j + m / 2, m / 2)

Základní případ, kde m = 1, má volání podprogramu copy_elems. V tomto základním případě jsou všechny prvky z dílčího pole i, které patří do bloku j, přidány najednou. Pokud to vede k tomu, že kbelík j bude mít příliš mnoho prvků, rozdělí to kbelík výše popsaným postupem.

Viz také

Reference