133 5 102MB
Norwegian Pages 298 Year 2003
■
■
Algoritmer og datastrukturer med eksempler i C og Java
1
i
• ••
tisip
- il og forskning
JL
GYLDENDAL AKADEMISK
© Forfatterne, Stiftelsen TISIP og Gyldendal Norsk Forlag AS 2003 I. utgave, 1. opplag 2003
ISBN 82-05-31144-7 Omslagsdesign: Kristin Berg Johnsen Layout: Designlaboratoriet Sats: Forfatterne Brødtekst: Times New Roman 10,5/12,5 Papir: 90 g Galerie One Trykk: AIT Gjøvik AS 2003 Boka er utgitt i samarbeid mellom Gyldendal Akademisk og Stiftelsen TISIP
Alle henvendelser om boka kan rettes til Gyldendal Akademisk Postboks 6730 St. Olavs plass 0130 Oslo www.gyldendal.no/akademisk [email protected] www.tisip.no
Mildrid Ljosland har fått støtte fra Det faglitterære fond Verken forfatterne. Stiftelsen TISIP eller Gyldendal Akademisk tar ansvar for at programmene i boka og på bokas internettside kan brukes til annet enn undervisningsformål.
Det må ikke kopieres fra denne boka i strid med åndsverkloven eller avtaler om kopiering inngått med KOPINOR. mteresseorgan for rettighetshavere til åndsverk. Kopiering i strid med lov eller avtale kan medføre erstatningsansvar og inndragning, og kan straffes med bøter eller fengsel.
Forord Denne boka beskriver mange standardalgoritmer og datastrukturer samt teknikker for å analysere algoritmenes tidsbruk. Målgruppen for boka er først og fremst studenter på høgskoler og universiteter, spesielt data- og informatikkstudenter. Men også andre som ønsker å drive med programmering på et seriøst nivå, trenger den kunnskapen denne boka formidler.
Vi har valgt å presentere algoritmene i to konkrete språk i stedet for å benytte pseudokode. Det er gjort fordi vi har sett at mange studenter strever med å omsette pseudokodebeskrivelser til programmeringskode. Vi tror det er lettere å holde fokus på prinsippene når de slipper prosessen med å omsette til programkode samtidig med at de skal tilegne seg det nye stoffet. De to språkene vi bruker, C og Java, gir oss muligheten til å presentere stoffet på to litt ulike måter, samtidig som en større målgruppe kan ha nytte av boka. Vi håper med denne boka å bidra til at norske studenter får en litt lettere jobb med å tilegne seg et til tider vanskelig, men viktig stoff. Lykke til med arbeidet! Trondheim, september 2003 Helge Hafting og Mildrid Ljosland
Innhold Innledning
1
1
7
Kompleksitetsanalyse 1.1 1.2
1.3
1.4
2
3
Innledning............................................................................................... Hva vi beregner..................................................................................... 1.2.1 Enkle operasjoner.................................................................... 1.2.2 Kompleksitet........................................................................... Asymptotisk notasjon........................................................................... 1.3.1 O - en øvre grense for kjøretid.............................................. 1.3.2 Q - en nedre grense forkjøretid........................................... 1.3.3 0 - en øvre og nedre grense................................................. Praktisk analyse..................................................................................... 1.4.1 Enkle tilfeller........................................................................... 1.4.2 Kode inni løkker..................................................................... 1.4.3 Noen «feller» og spesialtilfeller.......................................... 1.4.4 If-setninger og andre betingelser...........................................
8 8 8 9 11 11 12 13 14 14 15 16 16
Rekursjon
21
2.1
Hva er rekursjon?.................................................................................. 2.1.1 Eksempel: Fakultetsberegning.............................................. 2.1.2 Eksempel: A snu rekkefølgen av elementenei en tabell . . 2.1.3 Rekursjon og iterasjon........................................................... 2.1.4 Induksjon, rekursjon og rekursive definisjoner.................... 2.1.5 Analyse av lineær rekursjon................................................. 2.2 Splitt og hersk!..................................................................................... 2.2.1 Et rekursivt kall med halv størrelse .................................... 2.2.2 To rekursive kall med halv størrelse.................................... 2.2.3 To rekursive kall med størrelse (cirka) n — 1....................... 2.2.4 Mer enn to rekursive kall i hvert nivå ................................. 2.3 En generell metode for å finne tidskompleksiteten........................... 2.4 Prøve og feile-algoritmer..................................................................... 2.4.1 Eksempel: Åtte dronninger pået sjakkbrett.........................
22 22 24 26 27 27 29 29 31 33 36 39 42 42
Sortering
47
3.1 3.2 3.3
48 49 49 50 50
Sorteringsproblemet.............................................................................. Standardmetode for å bytte om tall..................................................... Innsettingssortering............................................................................... 3.3.1 Analyse..................................................................................... 3.3.2 Anvendelser..............................................................................
viii
Innhold
Boblesortering....................................................................................... 3.4.1 Analyse..................................................................................... 3.5 Velgesortering....................................................................................... 3.6 Om kvadratiske sorteringsalgoritmer................................................ 3.7 Shellsort................................................................................................. 3.7.1 Analyse..................................................................................... 3.8 Flettesortering....................................................................................... 3.8.1 Metoden flett........................................................................... 3.8.2 Metoden flettesort ................................................................. 3.9 Quicksort.............................................................................................. 3.9.1 Metoden quicksort................................................................. 3.9.2 Metoden median3sort - enkel forbedring av quicksort . . 3.9.3 Metoden splitt ........................................................................ 3.9.4 Mer om kjøretiden for quicksort........................................... 3.9.5 Feller når en implementerer quicksort................................. 3.10 Nedre grenser for kompleksitet,og lineær sortering........................ 3.10.1 En nedre grense for verste tidsbruk ved sortering............. 3.10.2 Sortering i lineær tid.............................................................. 3.11 Tellesortering....................................................................................... 3.11.1 Analyse..................................................................................... 3.11.2 Tellesortering av heltall.......................................................... 3.11.3 Intern tellesortering................................................................. 3.12 Radikssortering.................................................................................... 3.4
4 Datatypen liste 4.1 4.2
4.3
4.4
Liste som abstrakt datatype................................................................. Liste implementert ved hjelp aven tabell........................................... 4.2.1 Opprette lista, finne lengden av den og tømme den .... 4.2.2 Sette inn elementer og fjerne elementer fra lista................. 4.2.3 Finne et bestemt element i lista og sortere den.................... 4.2.4 Sekvensiell gjennomløping av lista........................................ Lenket liste.......................................................................................... 4.3.1 Enkel liste.................................................................................. 4.3.2 Dobbeltlenket liste................................................................. Iteratorer ..............................................................................................
5 Kø og stakk 5.1 5.2 5.3
Kø........................................................................................................... 5.1.1 Implementasjon ..................................................................... Stakk .................................................................................................... 5.2.1 Implementasjon ..................................................................... Eksempel: En enkel kaikulator..........................................................
51 51 52 53 54 55 55 56 57 58 59 60 62 64 66 67 67 68 68 69 70 70 72
77 78 79 79 80 81 82 83 83 88 92
97 98 98 100 100 102
Innhold IX
6 Trær 6.1 6.2
6.3
6.4
6.5
7
111 Grafer og trær........................................................................................ Binærtrær............................................................................................... 6.2.1 Høyde og dybde .................................................................... 6.2.2 Traversering ........................................................................... 6.2.3 Generelle trær representertsom binærtrær............................ Binære søketrær..................................................................................... 6.3.1 Innsetting................................................................................. 6.3.2 Søking ..................................................................................... 6.3.3 Sletting..................................................................................... Tidskompleksitet for operasjonerpå binærtrær.................................. 6.4.1 Dybde, høyde og traversering ............................................. 6.4.2 Innsetting, søking og sletting i binært søketre.................... B-trær..................................................................................................... 6.5.1 Operasjoner på B-trær.......................................................... 6.5.2 Kompleksitetsbetraktninger .................................................
112 115 117 119 121 122 123 123 124 126 127 127 128 130 134
Heapstrukturen og prioritetskøer
139
7.1
140 140 141 141 142 144 146 146 147 148 149
7.2
Innledning............................................................................................... 7.1.1 Hvaerenheap?....................................................................... 7.1.2 Anvendelser.............................................................................. Datastrukturog metoder ...................................................................... 7.2.1 Metoden fiks_heap................................................................. 7.2.2 Metoden lag_heap................................................................. 7.2.3 Metoden hent_maks.............................................................. 7.2.4 Metoder for å forandre prioritet.......................................... 7.2.5 Metoden sett_inn.................................................................... 7.2.6 Metoden heapsort.................................................................... 7.2.7 Noen merknader til implementasjonen.................................
8 Hashtabeller 8.1
8.2
8.3
Hva er en hashtabell?........................................................................... 8.1.1 Noen anvendelser.................................................................... 8.1.2 Lastfaktor................................................................................. 8.1.3 Problemer med hashtabeller................................................. Hashfunksjoner..................................................................................... 8.2.1 Hashfunksjon basertpå restdivisjon..................................... 8.2.2 Hashfunksjon basertpå multiplikasjon.................................. Kollisjonshåndtering........................................................................... 8.3.1 Lenkede lister........................................................................... 8.3.2 Åpen adressering....................................................................
9 Grafteori 9.1
Innledning.............................................................................................. 9.1.1 Hva en graf er........................................................................... 9.1.2 Definisjoner..............................................................................
153 154 154 155 155 156 157 157 160 160 160
169 170 170 170
X Innhold
Implementasjoner................................................................................. 9.2.1 Naboliste................................................................................. 9.2.2 Tabell........................................................................................ 9.2.3 Sammenlikning av naboliste og tabell................................ 9.3 Bredde først-søk (BFS)........................................................................ 9.3.1 Eksempel................................................................................. 9.3.2 Analyse.................................................................................... 9.4 Dybde først-søk (DFS)....................................................................... 9.4.1 Metoden dfs .......................................................................... 9.4.2 Metoden dfs_init.................................................................... 9.4.3 Metoden df_sok .................................................................... 9.4.4 Eksempel................................................................................. 9.4.5 Analyse.................................................................................... 9.5 Topologisk sortering .......................................................................... 9.5.1 Metoden df_topo.................................................................... 9.5.2 Metoden topologisort............................................................. 9.5.3 Litt mer forklaring, og et par eksempler............................. 9.5.4 Analyse.................................................................................... 9.6 Sammenhengende og sterkt sammenhengende grafer................... 9.6.1 Analyse.................................................................................... 9.7 Vektede grafer....................................................................................... 9.7.1 Datastruktur for vektet graf implementert som naboliste . 9.8 Korteste vei-problemet....................................................................... 9.8.1 Innledning .............................................................................. 9.8.2 Felles metoder....................................................................... 9.8.3 Dijkstras algoritme................................................................. 9.8.4 Bellman Ford-algoritmen....................................................... 9.9 Minimale spenntrær............................................................................. 9.9.1 Kruskals algoritme................................................................. 9.9.2 Prims algoritme....................................................................... 9.10 Maksimal flyt....................................................................................... 9.10.1 Flytnettverk.............................................................................. 9.10.2 Ford Fulkerson-metoden for maksimal flyt ....................... 9.10.3 Edmonds Karp-algoritmen.................................................... 9.2
10 Noen avanserte programmeringsteknikker 10.1
Ulike optimaliseringsmetoder............................................................. 10.1.1 Rå kraft-algoritmer................................................................ 10.1.2 Splitt og hersk-algoritmer ................................................... 10.1.3 Probabilistiske algoritmer ................................................... 10.2 Dynamisk programmering ................................................................ 10.2.1 To eksempler på dynamisk programmering...................... o 10.2.2 A kombinere rekursjon med dynamisk programmering . . 10.3 Grådige algoritmer............................................................................. 10.3.1 Huffman-koding.................................................................... 10.4 Evolusjonære algoritmer...................................................................
171 171 172 175 176 179 179 180 180 182 182 182 182 183 185 185 186 186 187 189 189 191 191 191 194 194 197 199 200 204 207 207 209 211
215 216 216 217 217 219 219 223 225 225 229
Innhold XI
10.4.1 Gener, formering, mutasjon ogutvelging ........................... 10.4.2 Genetisk algoritmeog genetiskprogrammering..................
11 Bruk av ferdige klassebibliotek
A
229 231
235
11.1 Datastrukturene konteiner og iterator................................................ 11.1.1 Iteratorer................................................................................. 11.1.2 Konteinere............................................................................. 11.2 Standard Template Library................................................................ 11.2.1 Konteinere............................................................................. 11.2.2 Iteratorer................................................................................. 11.2.3 Algoritmer............................................................................. 11.3 Java Collections Framework............................................................. 11.3.1 Konteinere............................................................................. 11.3.2 Iteratorer................................................................................. 11.3.3 Algoritmer............................................................................. 11.4 Eksempel: Frekvens av ord i en tekst...............................................
236 236 237 238 239 245 246 254 254 258 259 264
Matematikk
271
A.l A.2 A.3
272 272 272 272 2T5 273 273 273 274 274 274 275
Begreper................................................................................................ Notasjon................................................................................................ Formelsamling ................................................................................... A.3.1 Logaritmer............................................................................. A.3.2 Rekker.................................................................................... A.3.3 Geometriske rekker................................................................ A.3.4 Den harmoniske rekka.......................................................... A.3.5 Stirlings approksimasjon for nl............................................. A.4 Litt sannsynlighetsregning................................................................ A.4.1 Forventningsverdier ............................................................. A.5 Approksimere summer med integraler............................................ A.6 Det greske alfabetet.............................................................................
Litteratur
277
Algoritmer
279
Register
283
Innledning Denne boka handler om algoritmer og datastrukturer. En datastruktur beskriver hvordan en samling med data er organisert. Vi samler data om en person i en persondatavariabel (en struct i C og et objekt i Java), mens dataene om mange personer kan samles i en tabell av persondata. Begge deler er eksempler på datastrukturer, og vi skal se på mange forskjellige typer datastrukturer utover i boka. En algoritme er en trinnvis beskrivelse av hvordan et problem kan løses, og er grunnlaget for all programmering. Ordet algoritme er utledet fra navnet til den ara biske matematikeren og forfatteren Abu Jafar Mohammed ibn Musa al-Khowaritzmi, som levde fra om lag 780 til 850 og hadde sitt virke ved slottet i Bagdad. De fleste algoritmene trenger en datastruktur å jobbe mot, og datastrukturene tren ger algoritmer knyttet til seg for å kunne brukes til noe nyttig. Derfor er det naturlig å behandle de to emnene under ett. Noen ganger vil vi ha mest fokus på algoritme ne, som når vi ser på ulike sorteringsalgoritmer, og noen ganger på datastrukturene, som når vi ser på trær. Men uansett hvor fokus ligger, må vi ta hensyn til begge as pektene for å få et best mulig program.
Og hva er så et «godt» program? Som nybegynner var du sikkert fornøyd med programmet så snart du fikk det til å gjøre det du ville, og gi riktig svar. Etter hvert som programmeringsferdighetene utviklet seg, begynte du kanskje å tenke mer på hvordan programmet skulle bygges opp på en logisk og strukturert måte, slik at det ble lettere å sette seg inn i for andre. Det tredje trinnet i denne prosessen er å velge algoritmer og datastrukturer som er mest mulig hensiktsmessig for det problemet som skal løses. Men det forutsetter at du kjenner til ulike algoritmer og datastrukturer og vet hvordan de fungerer. Da kan du velge det som vil fungere best i hvert enkelt tilfelle. La oss se på noen eksempler.
Finne like dokumenter På Internett finnes flere milliarder nettsider, men noen av dem er like. For søkemotorleverandører er det et poeng å finne ut hvilke nettsider som er like, siden brukerne vil bli misfornøyd hvis for eksempel fem av de ti første treffene viser seg å være samme dokument med ulike nettadresser.
Tenk deg at du har fått i oppdrag å lage en algoritme som grupperer nettsider slik at like dokumenter kommer i samme gruppe. Vi definerer to dokumenter som like
Innledning
hvis hvert tegn i det ene dokumentet er lik det tegnet som kommer på samme plass i det andre dokumentet (jamfør likhet mellom tekststrenger).
Det er enkelt å lage et program som tar for seg ett og ett dokument og sammenlikner det med alle de andre dokumentene. Men hvis du prøver dette programmet på mer enn noen få dokumenter, vil du finne ut at programmet «henger» og ikke blir ferdig. Problemet er at med m dokumenter må vi foreta m2/2 sammenlikninger. Hvis vi antar at hver sammenlikning tar omtrent ett millisekund (inkludert å lese inn filen) og du skal gruppere en million dokumenter, vil det faktisk ta mange år å bli ferdig! Slike problemstillinger behandler vi i kapittel 1.
En bedre måte å gjøre det på er følgende: Regn ut et karakteristisk tall for hvert dokument. For eksempel kan du la hvert tegn bety et bestemt tall og summere alle eller noen av tegnene (mer avanserte metoder finnes også). Hvis tallene blir ulike, er dokumentene forskjellige. Hvis tallene er like, er dokumentene antakelig like, men behøver ikke nødvendigvis å være det. Derfor kan dokumentene i første omgang deles i grupper basert på dette karakte ristiske tallet. Dokumenter som havner i forskjellige grupper, er helt sikkert ulike, mens vi må foreta ytterligere beregninger for å fastslå om dokumenter i samme gruppe er like eller ikke.
For å dele i grupper vil det være lurt å bruke en hashtabell. Det er en datastruktur som du får høre mer om i kapittel 8. I hashtabeller kan vi plassere dokumentene slik at det er lett å finne igjen alle som har det samme karakteristiske tallet. Et annet alternativ kunne vært å sortere (se kapittel 3) dokumentene etter det karakteristiske tallet, for da ville jo de like havne ved siden av hverandre. Men vi er ikke inter essert i å vite om ett dokument er «større» enn et annet, bare om de er like eller ulike. Derfor vil sortering føre til at vi gjør mye unødig arbeid, og hashtabell er å foretrekke. Totalt får vi derfor følgende algoritme: 1. Regn ut det karakteristiske tallet for alle dokumentene.
2. Del i grupper avhengig av det karakteristiske tallet.
3. Sammenlikn dokumenter innenfor hver gruppe for å fastslå helt sikkert hvil ke som er like. Innenfor hver gruppe må vi sammenlikne alle dokumentene med alle andre, bortsett fra hvis vi allerede har fastslått at et dokument B er likt et annet dokument A. Da behøver vi ikke å sammenlikne de andre med både A og B, bare med ett av dem. Derfor gjelder det å få gruppene så små og homogene som mulig, noe vi til en viss grad kan styre ved å velge utregningsmåten for det karakteristiske tallet.
Med denne metoden vil det å gruppere en million dokumenter kunne gjøres på under en time hvis vi fortsatt antar at en sammenlikning tar om lag ett millisekund og det å regne ut det karakteristiske tallet tar omtrent det samme. Her forutsetter vi også at måten vi regner ut det karakteristiske tallet på, gjør at de fleste gruppene bare inneholder like dokumenter.
3 Lage ordbok Vi skal lage ei ordbok, og samler derfor tusenvis av ord. Til å lagre ordene tren ger vi en datastruktur. Siden ordboka skal finnes i papirversjon, trenger vi ordene alfabetisk sortert, og må ta hensyn til det når vi velger datastruktur.
En mulighet er å bruke en tabell eller en annen li ste struktur. I kapittel 4 gjennomgår vi ulike varianter og ser på fordeler og ulemper med hver av dem. En annen mulig het er å bruke en trestruktur. Trær behandles i kapittel 6. Et tre er en datastruktur der et element refererer til et eller flere andre elementer, som igjen kan referere til andre elementer. I vårt eksempel kan vi la bokstaver eller deler av ord være ele mentene. La oss si at vi har ordene treningsdrakt, treningsdress, treningssamling, treningsstudio og treningssykkel. Da kan vi la «trenings» være et element. Dette elementet kan referere til «dr» og «s». «dr» kan referere videre til «akt» og «ess», mens «s» refererer til «amling», «tudio» og «ykkel». På denne måten slipper vi å lagre «trenings» mange ganger, og sparer plass. I ei ordbok vil det alltid være mange ord med like forstavinger, slik at plassbesparelsen kan bli betydelig. Imidlertid kan trestrukturen føre til at noen algoritmer tar lengre tid enn de ville ha gjort i en enklere struktur. Det vil derfor være en avveining mellom lagerplass og prosessorhastighet om den ene eller den andre datastrukturen er å foretrekke. Slik vil det ofte være, og det er derfor et poeng å kjenne til mange ulike måter å løse et problem på, slik at du kan velge det beste i en konkret situasjon.
Lengden på det lengste ordet i ordboka vår kan vi finne ved følgende algoritme: Start på det første elementet i trestrukturen. Finn maksimallengden av hvert av de elementene det refererer til, og velg det lengste av dem. Adder denne lengden til elementets lengde. Hvordan finner vi så maksimallengden til de elementene det refererer til? Nøyaktig på samme måte, ved å se på lengdene til de elementetene disse refererer til. Slik fortsetter vi til det ikke lenger er flere elementer å referere til.
Dette er et eksempel på en rekursiv algoritme. Vi behandler rekursjon grundig i kapittel 2, og teknikken brukes i mange av de etterfølgende kapitlene. Blant annet vil trær på grunn av sin struktur ofte gi opphav til rekursive algoritmer.
Beregne kjørerute Bilcomputeren skal gi oss informasjon om raskeste kjørerute mellom der vi er i øyeblikket, og en angitt posisjon. Opplysninger om veier, veikryss og beregnet kjøretid mellom alle veikryssene lagres mest hensiktsmessig i en datastruktur som kalles en graf. Grafer behandler vi i kapittel 9. I dette kapitlet finner du blant an net en algoritme som finner korteste eller raskeste vei mellom to punkter, akkurat hva bilcomputeren trenger. Denne algoritmen bruker en prioritetskø som et hjelpe middel underveis i beregningene. En prioritetskø er en datastruktur der vi raskt kan finne det høyest prioriterte elementet. Den kan lages ved hjelp av en spesiell trestruktur som kalles en heap, og vi behandler begge disse begrepene i kapittel
4
Innledning
7. Korteste vei-algoritmen er også et eksempel på en programmeringsteknikk som kalles en grådig algoritme. Denne og andre litt spesielle teknikker ser vi nærmere på i kapittel 10.
Som du ser, henger algoritmene og datastrukturene sammen, og vi trenger ofte å ta i bruk flere forskjellige datastrukturer i samme program. Mange av de algoritmene og datastrukturene vi tar for oss i denne boka, finnes også i ferdige bibliotek knyttet til ulike programmeringsspråk. 1 kapittel 1 1 ser vi på to slike bibliotek, ett i C++ (det finnes ikke noe slikt bibliotek i C), og ett i Java. Når du seinere skal ta i bruk de algoritmene og datastrukturene du har lært om i denne boka, er det naturlig å bruke det som finnes i de ferdige bibliotekene. Men det er likevel et poeng å studere en «hjemmesnekret» versjon først, for da lærer du hvordan det implementeres, ikke bare hvordan det skal brukes. Dermed har du en mye bedre bakgrunn for å kunne vurdere hva som er hensiktsmessig i en gitt situasjon.
Litt om boka C og Java har litt forskjellig terminologi. C har begrepet funksjoner der Java har metoder. Dette skaper et lite problem når den samme algoritmen finnes både som C- og Java-kode. Vi har valgt konsekvent å bruke begrepet metode når vi refererer til koden. Kodeeksemplene er laget for å få fram algoritmene tydeligst mulig, og inneholder derfor ikke den feilhåndteringen man gjerne vil ha i et robust program. Algorit mene konsentrerer seg ofte om en enkelt metode, det er altså ikke komplette pro grammer. Alle kodeeksemplene kan imidlertid lastes ned i form av kildekode fra http://www.tisip.no(boker/algdat. Nedlastbar kode inneholder ofte et lite testprogram.
C-koden følger C99-standarden. For gcc betyr det at den må kompileres med opsjo nen -std=c99. Java-koden bruker Sun Java versjon 1.4. Hvis du har en kompilator som følger en eldre standard, må du være forberedt på at enkelte ting må endres litt. Den største endringen fra forrige versjon av C er at C99 tillater tabeller med vari able grenser. I de tilfellene vi har brukt det, må du som sitter på en eldre versjon, bruke malloc i stedet.
Vi regner med at du allerede har grunnleggende programmeringsferdigheter. Derfor forklarer vi stort sett ikke alminnelige språkkonstruksjoner, men konsentrerer oss om å forklare det som er temaet for kapitlet. Noen matematiske begreper er forklart i et vedlegg. Vi har brukt følgende symboler:
c Tips
C-kode
C++ kode
Javakode
Vanskelig oppgave
5 Mange av underkapitlene slutter med oppgaver som er relevante for dem. I tillegg slutter hvert kapittel med oppgaver som går på hele kapitlet og gjerne kombinerer stoff fra flere underkapitler. Løsninger på en del av oppgavene finnes på bokas nettsted. Der finner du også and re lenker, blant annet e-postadressene våre. Kontakt oss gjerne hvis du har kom mentarer til boka, oppgavene, løsningene eller annet. Det vil helt sikkert finnes trykkfeil i boka. Disse vil bli lagt ut på ei trykkfeil] i ste på nettstedet etter hvert som vi oppdager dem. Hvis du finner noe du mener må være en trykkfeil, sjekk først om den står på lista. Hvis ikke, send oss en e-post om saken.
Kompleksitetsanalyse
Læringsmål: Læringsmål for dette kapitlet er:
• å forstå kompleksitetsanalyse
- formålet: å forutsi tids- og ressursbruk - teoretisk grunnlag - noen enkle teknikker og regler
Kompleksitetsanalyse
1.1 Innledning I dette kapitlet ser vi på hvordan vi kan forutsi ressursbruk. Som regel er vi inter essert i kjøretid som funksjon av datamengde, men vi kan også se på plassbruk. Formålet med analysen er bl.a. å unngå uhensiktsmessige algoritmer, og å kunne svare på det velkjente spørsmålet «Hvorfor går dette programmet så tregt?».
En helt nøyaktig analyse kan dessverre bli svært komplisert, selv for enkle prob lemer. Kjøretid avhenger ikke bare av algoritmen og størrelsen på datamengden, men også hvordan datamengden ser ut. Vi gjør derfor en del forenklinger som gjør analysen mer uavhengig av detaljer i datamengden.
1.2 Hva vi beregner Med kompleksitetsanalyse prøver vi ikke å forutsi kjøretid i sekunder direkte. Det er ikke bare unødig komplisert, men også forskjellig for ulike maskiner. I stedet prøver vi å finne sammenhenger av typen «kjøretiden T for en algoritme er propor sjonal med f (n), der n er størrelsen på datamengden».
Ved hjelp av en slik sammenheng kan vi deretter estimere kjøretid i sekunder for datasett i alle størrelser ved hjelp av en testmåling på den aktuelle maskinen. Hvis f.eks. T (n) er proporsjonal med n1 2 for en algoritme, gjelder det for alle mas kiner som kjører denne algoritmen. Vi har altså T (zz) = kn2, der k er en maskinavhengig konstant. Vi kan finne k for en gitt maskin ved testing: Hvis målinger viser at det tar 1 l.y å behandle en datamengde med 1000 elementer på den aktuelle maskinen, får vi 1 Is = k ■ 10002 => k = . For denne maskinen vil 2000 ele
menter dermed ta T (2000) = k ■ 20002 = y--^ • 20002 = 44s. Tilsvarende vil det ta 99s å behandle 3000 elementer.
1.2.1 Enkle operasjoner Det vi ønsker å finne, er et maskinuavhengig mål for algoritmenes tidsforbruk. Hvis vi trenger å beregne faktisk tidsforbruk, kan det gjøres ut fra én eller noen få prøvekjøringer med forholdsvis små datasett.7
1 stedet for å telle sekunder, ser vi på algoritmen og teller opp hvor mange enkle operasjoner den utfører for en datamengde av en gitt størelse. Enkle operasjoner er slike ting som: • å summere to tall, «a + b», og andre aritmetiske operasjoner 1 Vær oppmerksom på at for små datasett ikke gir noe godt bilde av tidsforbruk, bl.a. fordi det er vanskelig å måle tiden med tilstrekkelig høy nøyaktighet.
Hva vi beregner
• tilordning, «a = b» • enkle tester, «if (a > b)»
For vår maskinuavhengige analyse antar vi at alle slike enkle operasjoner tar like lang tid. Virkeligheten er mer komplisert, men dette gir en god tilnærming. Vi teller rett og slett opp hvor mange enkle operasjoner programmet vårt gjør. Algoritme 1.1 Minimum av to tall a, b: de to tallene vi skal finne minimum av int mznfint a, int b) { if (a < b) return a-, ' else return b', 4}
: public static int mmfint a, int b) { if (a