**Timsort** on sorteerimisalgoritm, mis on saadud piste- ja ühildusmeetodeid kombineerides ning alusmeetodeid optimeerides ja täiendades. Timsort loodi 2002. aastal Pythoni programmeerimiskeele jaoks Tim Petersi poolt. Lisaks on Timsorti implementeeritud veel mitmed programmeerimiskeeled nagu Java, Swift, ja Rust. Timsort töötab jaga-ja-valitse algoritmi põhimõttel, mis lühidalt seisneb kolmes sammus: jaga sisendmassiiv väiksemateks massiivideks, sorteeri väiksemad massiivid kasutades kahendpistemeetodit, ühilda saadud väiksemad massiivid tagasi üheks tervikuks. Tulemuseks on sorteeritud massiiv. Timsort kasutab ära massiivis juba esinevaid sorteeritud lõike. Timsorti halvima juhu ajaline keerukus on O(*n* log *n*), parima juhu ajaline keerukus aga O(*n*).

Algoritmi kirjeldus
-------------------

Timsort on keeruline algoritm. Süvitsi mõistmiseks on kasulik algoritm jagada osadeks. Tim Peters lõi algoritmi põhimõttega, et sorteerides ära kasutada juba sorteeritud lõike, mis esinevad pärismaailmas kasutusel olevatel andmemassiivides. Implementatsiooniks on tarvilik luua kõigepealt meetod kahendotsinguks ning piste- ja ühildusmeetodid. Timsort on stabiilne sorteerimisalgoritm, st sama väärtusega elementide järjekord sorteerimisel ei muutu.

Enne detailsemat algoritmi kirjeldust oleks tarvis teada järgmiseid termineid ja nende põhimõtteid:

*näide: run (juba sorteeritud lõik massiivis)*
* *run* - kui massiivis on üle 64 elemendi, otsib Timsort massiivist natural *run*-e, mis on vähemalt kahest elemendist koosnevad juba sorteeritud massiivi lõigud, mis ongi algoritmi kontekstis run. Kui leitakse kahanevas järjekorras *run*, pööratakse see tagurpidi. Viited nendele lõikudele pannakse pinusse. Väiksematel (pikkus n < 64) massiividel rakendatakse lihtsalt pistemeetodit.

* *minrun* - massiivi pikkusest tuletatud arv vahemikus (32; 64], millega jagatise tulemusena massiivi pikkusega saadakse kas võrdne või väiksem arv mõnest kahe astmest. Algoritm valib selle arvu, sest suvalises jadas on pikkade *natural run-*ide esinemine ebatõenäoline. Kui *natural run-*is esineb vähem kui *minrun* elementi, pikendatakse see *run* *minrun* elementi pikaks. Suvalises massiivis selle tulemusena kipuvad kõik *run*-id olema pikkusega *minrun*.

Timsort kasutab pistemeetodit sorteerimaks massiive, milles on alla 64 elemendi. Pistemeetod on suurte massiivide puhul aeglane, ent õige implementatsiooni korral väikeste massiivide puhul väga kiire.

Esimene samm: *minrun*-i leidmine

Teine samm: sisendmassiivi tükeldamine *run*-ideks ning nende sorteerimine

1. alustame massiivi algusest, otsides *natural run-*e
2. kui hetkene *run* on väiksem kui *minrun*, võta järgnevaid elemente kuni selle *run-*i pikkus on *minrun*
3. saadud *run* sorteeritakse pistemeetodiga

Kolmas samm: sorteeritud *run*-ide ühildamine

1. ideaalis peaks massiivis olema sarnase pikkusega sorteeritud *run*-id
2. loo tühi ennikutest (*run-*i algusaadress, *run*-i pikkus) koosnev pinu
3. hakka lisama *run-*e järjest pinusse
4. iga pinusse lisamise puhul tuleb kontrollida, pinu peal olevad *run-*id vastavad järgnevatele tingimustele:

Tingimused, mida Timsort kasutab määratlemaks, kas *run-*e pinu peal peab omavahel ühendama:

*run-*id pannakse pinusse. Kui |Z| ≤ |*Y*| + |*X*|, siis X ja Y ühildatakse ning pannakse tagasi pinusse. Ühildumine jätkub, kuni kõik *run-*id täidavad tingimusi i. |Z| > |X| + |Y| ja ii. |Y|>|X|
Olgu X, Y, Z pinu pealmised *run-*id. Püstkriipsud *run-*i ümber tähendavad *run-*i pikkust, nt |X|.

i. |Z| > |X| + |Y|

ii. |Y| > |X|

Kui üks neist tingimustest on rikutud, ühendatakse omavahel Y ja lühem X või Z seast.

Seda sammu korratakse kuni kogu pinu täidab neid tingimusi.

Käsitlemata *run*-ide puhul vaadeldakse pinu uuesti läbi, tehes vahetusi ja ühildamisi kui vaja.

Kolmanda sammu eesmärk on hiljem ühildusmeetodiga ühendada järjest sarnasema pikkusega *run-*id, et ühildumine oleks efektiivne.

Näide: oletame, et meil on ideaalne juhus, meil on 8 *run*-i suurusega 128, 64, 32, 16, 8, 4, 2, 2 (järjekord on tegelikkuses suvaline). Algoritm ei ühenda ühtegi *run-*i enne, kui 2 ja 2 saavad pinus *run-*e kontrollides kokku. Sellele järgneb 7 perfektset, efektiivset ühendamist.

Neljas samm:

Eelnevas sammus sorteerisime ja ühendasime erinevaid *run-*e, et saada pinusse optimaalne viimane *run-*ide ühendamise järjekord, lähtuvalt pikkustest (et lõplik ühendamine toimuks võimalikult sarnase pikkusega *run-*idest).

Nüüd hakkame järjest ühendama kahe haaval järelejäänud *run-*e, lõpuks saades sorteeritud massiivi.

1. loome ajutise *run-*i mille suurus on allesjäänud *run-*idest vähima pikkus
2. paneme kõik elemendid lühemast *run*-ist ajutisse *run-*i
3. võrdleme suurema ja ajutise *run-*i elemente:

i. neist väiksema asetame lühemasse *run-*i (need elemendid on ajutises olemas)

ii. seame järgmiseks vaadeldavaks elemendiks ära võetud elemendist järgmise

iii. kordame kuni üks *run-*idest (suurem või ajutine) on täielikult läbi vaadatud

Tulemusena lõpuks on sorteeritud massiiv.

Neljandas sammus kasutab algoritm ühildusmeetodit, kuid mitte tavalist - seda on kohandatud, suurendades efektiivsust, kasutamaks eelnevalt leitud *run-*e. See tähendab, et kuigi algoritm justkui ütleb neljandas sammus, et hakkame ükshaaval võrdlema kahe *run-*i elemente, siis tegelikult Timsort teeb seda efektiivsemalt. Näide: olgu meil *run-*id elementidega [1, 2, 3, 4, … 100] ning [101, 102, 103, .. 200]. Ebaefektiivne oleks neid üksikute võrdluste põhjal hakata ühildama.

Timsort kasutab selleks võtet, mida kutsutakse nimega *galloping.* Eesti keeles samuti galopp, tähendab see sisuliselt seda, et elemente ei tõsteta ühildatud, sorteeritud *run-*i mitte ükshaaval, vaid lõikude kaupa, mis kiirendab protsessi, kuna individuaalsete võrdluste arv on vähendatud. Lõigud leitakse kasutades kahendotsingut.

Näide, et toimuvat illustreerida paremini:

Olgu meil *run* X = [1, 2, 3, 4, 5, .. 100] ning Y = [101, 102, 103, .. 200].

Kõik punasega märgitud elemendid on väiksemad kui 21, mistõttu neid võib korraga paigutada lõpliku massiivi.
Esimesed 7 (eelnevalt defineeritud algne galopi sammu suurus) elementi *run-*ist X võrreldakse *run* Y esimese elemendiga. 101 > 7, 6, 5… Kõik need *run* X elemendid on väiksemad kui mistahes Y elemendid, mistõttu võib kõik need tõsta sorteeritud *run-*i. Vaatluse alla võetakse järgmised *run* X elemendid 8, 9, 10 jne ning võrreldakse taaskord Y esimese elemendi, 101-ga. Galopi samm muutub järjest suuremaks, et leida veel suuremaid lõike, mille saab sorteeritud *run-*i tõsta.

Joonisel on näha, et Y elemendid 9, 12, 13, 14, 15 on kõik väiksemad kui ajutises *run*-is olev element 21, mistõttu võib kõik punaseks värvitud Y elemendid tõsta sorteeritud *run-*i.

Andmete tükkide kaupa liigutamine on märgatavalt kiirem kui elementide ükshaaval võrdlemine ja tõstmine, mis annabki Timsordi tema efektiivsuse ja kiiruse.

Keerukused
----------

**Ajalised keerukused:**

* Halvim juht: O(*n* log *n*)

* Parim juht: O(*n*)

* Keskmine juht: O(*n* log *n*)

**Ruumiline keerukus:** O(*n*)

Verifikatsioon
--------------

2015. aastal leidsid hollandi ja saksa teadlased tavapärases Java Timsordi implementatsioonis vea ning andsid välja verifitseeritud parandatud algoritmi.

2018. aastal tõestati Timsordi halvima juhu ajaline keerukus.

Viited
------

1. ↑  Zhang, Yu; Zhao, Yongwang; Sanan, David (8. detsember 2018). "A Verified Timsort C Implementation in Isabelle/HOL". *arXiv.org* (inglise). DOI:10.48550/arxiv.1812.03318. Vaadatud 3. jaanuaril 2024.
2. 1 2  Auger, Nicolas; Jugé, Vincent; Nicaud, Cyril; Pivoteau, Carine (2018). "On the Worst-Case Complexity of TimSort". *DROPS-IDN/v2/document/10.4230/LIPIcs.ESA.2018.4* (inglise). Schloss-Dagstuhl - Leibniz Zentrum für Informatik. DOI:10.4230/LIPIcs.ESA.2018.4.
3. ↑  "Loading..." *bugs.openjdk.org*. Vaadatud 3. jaanuaril 2024.
4. ↑  "Is sort() stable in Swift 5?". *Swift Forums* (inglise). 4. juuli 2019. Vaadatud 3. jaanuaril 2024.
5. ↑  "slice - Rust". *doc.rust-lang.org*. Vaadatud 3. jaanuaril 2024.
6. 1 2  "Timsort — "The Fastest Sorting Algorithm" – The IE Blog" (Ameerika inglise). 3. detsember 2019. Vaadatud 3. jaanuaril 2024.
7. 1 2  "cpython: 034b077d3015 Objects/listsort.txt". *web.archive.org*. 28. jaanuar 2016. Originaali arhiivikoopia seisuga 28. jaanuar 2016. Vaadatud 3. jaanuaril 2024.`{{netiviide}}`: CS1 hooldus: robot: algse URL-i olek teadmata (link)
8. ↑  "TimSort - Data Structures and Algorithms Tutorials". *GeeksforGeeks* (Ameerika inglise). 19. mai 2017. Vaadatud 3. jaanuaril 2024.
9. ↑  de Gouw, Stijn; Rot, Jurriaan; de Boer, Frank S.; Bubel, Richard; Hähnle, Reiner (2015). Kroening, Daniel; Păsăreanu, Corina S. (toim-d). "OpenJDK's Java.utils.Collection.sort() Is Broken: The Good, the Bad and the Worst Case". *Computer Aided Verification*. Lecture Notes in Computer Science (inglise). Cham: Springer International Publishing: 273–289. DOI:10.1007/978-3-319-21690-4\_16.