Sorteringsalgoritmer Af Tobias André Højsgaard Klasse 2.4 Roskilde Tekniske Gymnasium IT Opgavens mål Denne rapport vil beskæftige sig med hvordan computere bruger matematiske algoritmer til at sortere tal efter størrelse. Målene for opgaven er at fremstille og beskrive tre forskellige typer af sorteringsalgoritmer: Bubble-up sort, insertion sort og quick sort. Ordet beskrive indebærer også at sammenligne de forskellige funktioners kørselstider vha. antallet af træk de har brugt på at flytte alle tallene hen på deres retmæssige plads. De tre algoritmer skal køres på fire forskellige typer af lister: En helt tilfældig liste, en næsten sorteret liste, en omvendt sorteret liste og en liste med mange ens værdier, som alle skal genereres af programmet da de skal være 10000 tal store. Sorteringsalgoritmerne Beskrivelse Vi har som sagt 3 forskellige typer af sorteringsalgoritmer. De forsøger alle at organisere tallene fra mindst til størst. Bubble-up sort er en algoritme, der sammenligner talpar gennem listen og ser på hvilken af dem, der er størst. Hvis tallet, der står forrest er større end det, der kommer efter det, så bliver de to tal byttet om. Insertion sort tager et tal i begyndelsen af listen, sammenligner med resten af listen indtil den finder et tal, der er mindre end den selv og placerer dette tal rigtigt i forhold til det tal, der står i begyndelsen af listen. Når det tal, der er mindst i hele listen er placeret som nummer 1 vil listen tjekke i forhold til tal nr. 2 indtil det tal der står på denne plads er det næstmindste og så videre. Quick sort tager et tal og behandler det som et omdrejningspunkt. Den placerer derefter alle de tal, der er mindre end omdrejningstallet i en liste for sig og alle tal, der er større, for sig. Quick sort kører desuden sig selv igen på de to nye lister den har lavet sådan at de bliver splittet op igen og igen indtil der kun er én ting tilbage i dem. Implementering Sorteringsalgoritmerne kan findes under den samlede kildekode i bunden af rapporten. Bubble-up er nok den sorteringsalgoritme med det simpleste princip bag sig. Vi bruger listeindekser til at sammenligne de ting der står på en plads i listen og på pladsen lige efter ved hjælp af en 'for'-løkke. Side 1 af 11
Det algoritmen gør er at sige, at alle tal på en vilkårlig listeplads skal sammenlignes med tallet på listepladsen med ét nummer højere end dem. På den måde går vi hele listen igennem og sammenligner. Vi har desuden lavet en 'try' og 'except'-kode. Grunden til dette er at algoritmen ikke ville kunne finde ud af hvad den skulle gøre når den kom til slutningen af listen og den ikke længere kan lægge 1 til det listenummer, hvorpå det tal den skal bruge står. Kombinationen af 'except' og 'break' betyder at funktionen genstarter når den ikke kan udføre koden under 'try' Insertion sort kører også med en 'for'-løkke. Vi giver en variabel en værdi, der er den samme som det tal, der står på listepladsen vi undersøger. Samtidig giver vi en variabel 'i' en værdi der er 1 mindre end listepladsens indeksnummer, hvilket senere lader os placere tal bag ved det første tal i listen. Vi siger derefter at så længe listens indeksnummer er større end eller lige med 1 (fordi 'i' er lige med listens indeks minus 1) så hvis værdien vi kigger på er mindre end tallet bag den vil de to blive rykket om, lidt ligesom en omvendt bubble-up sort. Quick sort bruger rekursive funktioner. Vi starter med at lave tre variabler og sætter dem til at være en tom liste: En mindre, lige med og større liste, som kan opbevare de tal vi fordeler i funktionen. Da disse lister er lokale i funktionen vil det ikke betyde noget at der kun er tre. Hvis længden af den liste vi skal undersøge er større end 1, så vil vi 'appende' større tal til den større liste osv. ved at sammenligne 'x' i 'for' løkken med omdrejningstallet som i denne funktion hedder 'midt'. De rekursive funktioner kommer ind i billedet når vi returnerer de tre lister plusset sammen, men samtidig kører funktionen på den større og mindre liste for at sortere dem. Hvis listens længde kun er på ét tal, så returnerer vi bare listen, hvilket derefter får funktionen til at sætte alle de små lister sammen til én stor, som så er i rigtig rækkefølge. Listegeneratorer Implementering Python giver mulighed for at skrive eller læse til tekstfiler ved at bruge funktionen: open("filnavn.txt","mode") Med mode menes om der skal skrives eller læses fra teksten. Til skrivnings bruges "w" og til læsning "r". En fælles størrelsesvariabel gør det muligt at justere længden af teksterne mere effektivt, samt hvilken rækkevidde de skal have mht. talstørrelser inde i listen. En tilfældig liste laves ved at bruge random.randrange() funktionen som tager et tilfældigt tal inden for en bestemt rækkevidde. Her er rækkevidde 1 til værdien af størrelsesvariablen. Vi skriver dette tilfældige tal ind i tekstfilen og bruger en variabel 'tal' til at tælle ned, så vi sikrer os at vi får den rette mængde tal i listen uden at ændre på størrelsesvariablens værdi. Den næsten sorterede liste laves efter næsten samme princip. Vi sørger bare for at rækkevidden af de tilfældige tal er mere justeret i forhold til listen. Her er listen opdelt i 10 intervaller som alle har deres egen rækkevidde at tage fra, sådan at all 1000-taller står før 2000-tallerne. Side 2 af 11
Den omvendt sorterede liste laves ved at sætte værdien af den variabel vi skriver ind i filen lige med 'tal' som vi jo bruger til at tælle ned fra den værdi størrelsen af listen antager. I dette tilfælde 10000, men det kunne lige så godt være 1000. Lister med mange ens værdier laves på præcis samme måde som den tilfældige liste, bortset fra at rækkevidden er mindre, så der er færre muligheder at tage fra når der skal findes tilfældige værdier. Sammenligning af lister: Det lykkedes desværre kun at implementere kode, der lod mig måle på antallet af træk i bubble-up og quick sort algoritmerne. Af grunde der ikke indtil videre er opklarede vises antallet af træk for insertion sort altid som 0. 10000 tal er en meget stor liste, så der er selvfølgelig en mængde af træk tilsvarende til denne størrelse. Sammenligningen for bubble-up sort er som følger: Vi ser at den omvendt sorterede liste er den største med over 45 millioner træk, hvilket stemmer overens med det faktum, at bubble-up sort skal køre igennem funktionen flere gange hvis der er et stort tal i begyndelsen af listen, fordi den hele tiden sammenligner med enkelte talpar og bytter rundt. Der er dog et problem med at udføre videre analyse af bubble-up sort med 10000-tal-lister. Den omvendt sorterede liste er for stor til at kunne blive beregnet af computeren eller også er der en fejl i programmet når det kommer til quick sort algoritmen, hvilket giver et upræcist sammenligningsgrundlag, eller retter, et ikke eksisterende sammenligningsgrundlag. Jeg har foretaget den samme sortering igen, men med lister med en længde af 1000 tal, hvilket kunne ændres simpelt på grund af implementeringen af en størrelsesvariabel i begyndelsen af listekoden. Side 3 af 11
Resultaterne er cirka de samme, men nedskaleret med en faktor 10. Vi ser her resultaterne for quick sort og insertion sort. Vi kan ikke rigtigt konkludere på insertion sort, da de lave resultater skyldes en fejl. For øvrigt er dette ikke noget, der er relateret til den måde jeg har stillet min sammenligningskode op. Jeg har udkommenteret de andre algoritmers kørsel fra den nederste del af programmet under test-fasen fordi det er klart, at når man kører to algoritmer på den samme tekstfil, så vi den ene vise 0 da den anden allerede har sorteret listen. Pointen er at insertion sort ikke har virket efter hensigten når det kommer til trækoptællingen. Morsomt nok er det også den omvendt sorterede liste, der er størst for quick sort. Dette har noget at gøre med, at jeg i stedet for at vælge et tal i midten af listen som mit omdrejningspunkt, har jeg i stedet valgt det Side 4 af 11
første tal. Dette betyder at denne funktion kører næsten ligesom bubble-up i den omvendt sorterede liste og derfor er langsom. Derudover er der kun én ting at sige, og det er at quick sort har et lavere antal træk for hver eneste liste. Da vi ikke kan konkludere på insertion sort algoritmen må den nuværende konklusion være at quick sort er den bedste sorteringsalgoritme. Samlet kildekode # -*- coding: cp1252 -*- #Vi importerer moduler til tilfældighed og tidsmåling import random import time #Listekode strrelse = 10000 #strrlse er listernes fælles størrelse lister = "us" #lister er usorterede #Vi bruger dette til at skifte mellem til og fra sorteret tilstand om lidt #De fire tekstfil åbnevariabler til skrivning skt = open("til.txt", "w") stn = open("nsl.txt", "w") skn = open("sor.txt", "w") skm = open("mev.txt", "w") #De fire åbnevariabler til læsning lst = open("til.txt", "r") ltn = open("nsl.txt", "r") lsn = open("sor.txt", "r") lsm = open("mev.txt", "r") Listegeneratorer #Tilfældig def til(): skt = open("til.txt", "w") tal = strrelse #Vi tæller ned fra den specificerede længde af listen på 10000 while tal > 0: ran = random.randrange(1,strrelse,1) skt.write(str(ran)+"\n") tal -= 1 Side 5 af 11
skt.close() #Næsten sorteret def nsl(): stn = open("nsl.txt", "w") tal = 0 reftal = strrelse while strrelse > tal: #Vi tildeler variablen skn værdier, #som stemmer overens med forskellige procentuelle værdier if tal >= reftal*0.9: ran = random.randrange(reftal*0.9,reftal,1) elif tal >= reftal*0.8 and tal < reftal*0.9: ran = random.randrange(reftal*0.8,reftal*0.9,1) elif tal >= reftal*0.7 and tal < reftal*0.8: ran = random.randrange(reftal*0.7,reftal*0.8,1) elif tal >= reftal*0.6 and tal < reftal*0.7: ran = random.randrange(reftal*0.6,reftal*0.7,1) elif tal >= reftal*0.5 and tal < reftal*0.6: ran = random.randrange(reftal*0.5,reftal*0.6,1) elif tal >= reftal*0.4 and tal < reftal*0.5: ran = random.randrange(reftal*0.4,reftal*0.5,1) elif tal >= reftal*0.3 and tal < reftal*0.4: ran = random.randrange(reftal*0.3,reftal*0.4,1) elif tal >= reftal*0.2 and tal < reftal*0.3: ran = random.randrange(reftal*0.2,reftal*0.3,1) elif tal >= reftal*0.1 and tal < reftal*0.2: Side 6 af 11
ran = random.randrange(reftal*0.1,reftal*0.2,1) else: ran = random.randrange(reftal*0,reftal*0.1,1) #Vi går hele vejen ned til 0 #NOTE: Denne kode er unødvendigt lang #Det er muligt at lave næsten sorterede lister nemmere #Denne kode var bare en kompliceret udgave af en generator #Omvendt rækkefølge def sor(): skn = open("sor.txt", "w") tal = strrelse while tal > 0: ran = tal skn.write(str(ran)+"\n") tal -= 1 #Da vi allerede tæller ned kan vi bare sætte listens værdi lige med tallet #Mange ens værdier def mev(): skm = open("mev.txt", "w") tal = strrelse while tal > 0: ran = random.randrange(1,strrelse*0.1,1) #Denne listes tilfældige rækkevidde er meget mindre og der er derfor mange ens værdier skm.write(str(ran)+"\n") tal -= 1 skm.close() Sorteringsalgoritmer #Bubble-up sort: def bubs(li): global t c = 1 while c > 0: for i in range(len(li)): Side 7 af 11
#Prøv at køre. Gentager når man kommer til enden af listen. try: if li[i] > li[i+1]: plc1 = li[i] plc2 = li[i+1] li[i] = plc2 li[i+1] = plc1 c += 1 t += 1 else: c -= 1 except: break #Break: Funktionen starter forfra hvis den ikke kan køre 'try' kode. return li #Insertion sort: def inss(li): c = 1 for x in range(1,len(li)): v = li[x] i = x - 1 while i >= 0: if v < li[i]: li[i+1] = li[i] li[i] = v i -= 1 t += 1 else: break return li #Quick sort def quis(li): global t #Vi globaliserer vores tællevariabel (t) så rekursive funktioner ikke genstarter den mindre = [] lige = [] strre = [] if len(li) > 1: Side 8 af 11
midt = li[0] for x in li: if x < midt: mindre.append(x) t += 1 elif x > midt: strre.append(x) t += 1 elif x == midt: lige.append(x) t += 1 return quis(mindre)+lige+quis(strre) else: return li return li Sortering #Generering af lister #Vi er nødt til at køre de fire funktioner for at generere listerne til() nsl() sor() mev() #Koden readlines bruges til at læse.txt filen én linje ad gangen #På den måde får vi hele tallene i stedet for cifrene og \n Sammenligning #Bubble-up sort sammenligning "Bubble-up sort sammenligning:" bubs(lst.readlines()) "Tilfældig liste:" "Antal træk:", t bubs(ltn.readlines()) "Næsten sorteret liste:" Side 9 af 11
"Antal træk:", t bubs(lsn.readlines()) "Omvendt sorteret liste:" "Antal træk:", t bubs(lsm.readlines()) "Liste med mange ens værdier:" "Antal træk:", t #Insertion sort sammenligning "Insertion sort sammenligning:" inss(lst.readlines()) "Tilfældig liste:" "Antal træk:", t inss(ltn.readlines()) "Næsten sorteret liste:" "Antal træk:", t inss(lsn.readlines()) "Omvendt sorteret liste:" "Antal træk:", t inss(lsm.readlines()) "Liste med mange ens værdier:" "Antal træk:", t #Quick sort sammenligning "Quick sort sammenligning:" quis(lst.readlines()) "Tilfældig liste:" Side 10 af 11
"Antal træk:", t quis(ltn.readlines()) "Næsten sorteret liste:" "Antal træk:", t quis(lsn.readlines()) "Omvendt sorteret liste:" "Antal træk:", t quis(lsm.readlines()) "Liste med mange ens værdier:" "Antal træk:", t Konklusion Vi har lavet fire lister af 10000 tal, men dette skabte i nogle tilfælde fejl for computeren, så i sammenligningen af algoritmerne blev denne værdi nedsat til 1000 tal. Vi kan konkludere på baggrund af resultaterne at quick sort har et lavere antal træk i hver eneste listetype. Dog ser den ud til at have et problem når listerne kommer op omkring de 10000 tal i længde, så efter dette punkt er bubble sort den mest medgørlige algoritme med omvendt sorterede lister, selvom træk-antallet og tiden for funktionens udførelse er meget stort/lang. Side 11 af 11