Tema
/
Algoritmai ir duomenų struktūros GO
Pamoka 7 / 15 Elementarus rūšiavimas ~60 min
Teorija

Jis jau yra standartinėje bibliotekoje

Praeitas žingsnis baigėsi klausimu: kodėl įterpimo rikiavimas beveik surikiuotiems duomenims laimi prieš sort.Slice iki maždaug 2 000, o paskui pralaimi?

Atsakymo nerasi savo kode. Jis yra Go standartinės bibliotekos kode.

sort.Slice viduje

sort.Slice iškviečia pdqsort — greitojo rikiavimo atmainą, kurią sutiksi kitą pamoką. Bet jos pirmasis sprendimas toks:

// src/sort/zsortfunc.go
const maxInsertion = 12

for {
    length := b - a
    if length <= maxInsertion {
        insertionSort_func(data, a, b)
        return
    }
    ...
}

Kai atkarpa sumažėja iki dvylikos elementų ar mažiau, pdqsort nustoja skaidyti ir iškviečia įterpimo rikiavimą.

Ne kaip atsarginį variantą. Kaip įprastą, dažniausiai vykdomą kelią: skaidydamas masyvą per pusę, algoritmas didžiąją dalį laiko dirba su mažomis atkarpomis.

sort.SliceStable — dar tiesmukiškiau

// src/sort/zsortfunc.go
func stable_func(data lessSwap, n int) {
    blockSize := 20
    a, b := 0, blockSize
    for b <= n {
        insertionSort_func(data, a, b)
        a = b
        b += blockSize
    }
    ...
}

Stabilus standartinės bibliotekos rikiavimas pradeda nuo to, kad įterpimo rikiavimu sutvarko kiekvieną 20 elementų bloką, ir tik paskui juos sulieja.

Ir dar viena vieta — partialInsertionSort_func, kviečiama su komentaru „The slice is likely already sorted". Tai tas pats greitkelis, kurį ką tik pamatavai savo kode.

Tai kodėl sort.Slice laimi prie n = 10 000

Ne todėl, kad ji nenaudoja įterpimo rikiavimo. Todėl, kad ji naudoja jį tik ten, kur jis geras — mažoms atkarpoms — o dideliam masyvui pirmiau pritaiko skaidymą, kurio tu dar nemoki.

Ji nėra alternatyva įterpimo rikiavimui. Ji yra įterpimo rikiavimas plius kai kas daugiau, ir tas „kas daugiau" yra kita pamoka.

Išvada, kurią verta įsidėmėti

Elementarūs rikiavimai nėra pralaimėjusios praeities technologijos. Jie yra komponentai. Jie pralaimi kaip savarankiškas sprendimas dideliems duomenims ir laimi ten, kur duomenų mažai arba jie beveik tvarkingi — o būtent tokie atvejai sudaro didžiąją dalį darbo bet kuriame rimtame rikiavime.

Burbuliukų rikiavimas šioje istorijoje nedalyvauja. Jis nėra niekur viduje, ir jo vienintelė vieta yra ši pamoka: jis rodo, kaip atrodo O(n²) tada, kai niekas jo nesušvelnina.

Spąstai

Skaičiai šiame žingsnyje paimti iš Go 1.26.4 šaltinio šioje mašinoje (src/sort/zsortfunc.go), ne iš dokumentacijos ir ne iš atminties. maxInsertion = 12 ir blockSize = 20 yra tos versijos konstantos; kitoje Go versijoje jos gali skirtis. Pasitikrink pats — šaltinis įdiegtas kartu su Go.