Ekvivalensrelationer

Relevanta dokument
Föreläsning 4 Datastrukturer (DAT037)

Explorativ övning 9 RELATIONER OCH FUNKTIONER

ADT Prioritetskö. Föreläsning 13 Innehåll. Prioritetskö vs FIFO-kö. Prioritetskö Exempel på användning. Prioritetsköer och heapar

Föreläsning 9 Datastrukturer (DAT037)

Seminarium 13 Innehåll

TDDI16 Datastrukturer och algoritmer. Prioritetsköer, heapar, Union/Find

Föreläsning 9 Innehåll

ADT Prioritetskö. Föreläsning 12 Innehåll. Prioritetskö. Interface för Prioritetskö. Prioritetsköer och heapar

Föreläsning 10 Innehåll. Prioritetsköer och heapar. ADT Prioritetskö. Interface för Prioritetskö. Exempel på vad du ska kunna

Datastrukturer, algoritmer och programkonstruktion (DVA104, VT 2015) Föreläsning 6

Inlämningsuppgiften. Föreläsning 9 Innehåll. Träd. Datastrukturer i kursen

Föreläsning 4 Datastrukturer (DAT037)

Programkonstruktion och Datastrukturer

Datastrukturer i kursen. Föreläsning 8 Innehåll. Träd rekursiv definition. Träd

Lösningsförslag för tentamen i Datastrukturer (DAT037) från

Föreläsning Datastrukturer (DAT036)

Föreläsning 13 Innehåll

Sökning och sortering

Föreläsning 9 Innehåll

Datastrukturer. föreläsning 10. Maps 1

Informationsteknologi Tom Smedsaas 19 augusti 2016

Tentamen Datastrukturer, DAT037 (DAT036)

Föreläsningsanteckningar F6

Föreläsning 2 Datastrukturer (DAT037)

Lösningsförslag till tentamen Datastrukturer, DAT037 (DAT036), Tiden det tar att utföra en iteration av loopen är oberoende av värdet på

Upplägg. Binära träd. Träd. Binära träd. Binära träd. Antal löv på ett träd. Binära träd (9) Binära sökträd (10.1)

Algoritmanalys. Inledning. Informationsteknologi Malin Källén, Tom Smedsaas 1 september 2016

Trädstrukturer och grafer

Föreläsning 1 Datastrukturer (DAT037)

Tentamen, Algoritmer och datastrukturer

Föreläsning Datastrukturer (DAT036)

TDDI16 Datastrukturer och algoritmer. Algoritmanalys

ID1020: Union-Find. Dr. Jim Dowling kap Slides adapted from Algorithms 4 th Edition, Sedgewick.

Datastrukturer. föreläsning 3. Stacks 1

13 Prioritetsköer, heapar

Några svar till TDDC70/91 Datastrukturer och algoritmer

Lösningsförslag till tentamen Datastrukturer, DAT037,

Tentamen Datastrukturer D DAT 035/INN960

TENTAMEN: Algoritmer och datastrukturer. Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad.

Tentamen Datastrukturer (DAT036)

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 8 Erik Nilsson, Institutionen för Datavetenskap, LiU

Tentamen Datastrukturer för D2 DAT 035

Tentamen Datastrukturer D DAT 035/INN960

Tentamen Datastrukturer (DAT036)

Självbalanserande träd AVL-träd. Koffman & Wolfgang kapitel 9, avsnitt 1 2

Föreläsning 5: Grafer Del 1

Algoritmer och datastrukturer 2012, fo rela sning 8

Dugga Datastrukturer (DAT036)

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Tentamen Datastrukturer (DAT036/DAT037/DIT960)

Abstrakta datatyper. Primitiva vektorer. Deklarera en vektor

Föreläsning 7. Träd och binära sökträd

Föreläsning 13. Träd

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 9 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Binära sökträd. Seminarium 9 Binära sökträd Innehåll. Traversering av binära sökträd. Binära sökträd Definition. Exempel på vad du ska kunna

Träd Hierarkiska strukturer

Programmering för Språkteknologer II. Innehåll. Associativa datastrukturer. Associativa datastrukturer. Binär sökning.

Föreläsning 2 Datastrukturer (DAT037)

Exempeltenta GruDat 2002/2003

Självbalanserande AVL-träd Weiss, avsnitt 4.4

Föreläsning 11 Datastrukturer (DAT037)

Tabeller. Programkonstruktion. Moment 8 Om abstrakta datatyper och binära sökträd. Implementering av tabellen. Operationer på tabellen

Lösningar Datastrukturer TDA

Linjärt minne. Sammanhängande minne är ej flexibelt. Effektivt

Föreläsning Datastrukturer (DAT036)

Tommy Färnqvist, IDA, Linköpings universitet

Ett generellt träd är. Antingen det tomma trädet, eller en rekursiv struktur: rot /. \ /... \ t1... tn

Föreläsning Datastrukturer (DAT036)

Exempel: Förel Rekursion III Nr 14. Uno Holmer, Chalmers,

Algoritmer, datastrukturer och komplexitet

Tillämpad Programmering (ID1218) :00-13:00

Föreläsning 5: Giriga algoritmer. Kruskals och Prims algoritmer

DAI2 (TIDAL) + I2 (TKIEK)

Föreläsning 5 Innehåll

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 8 Om abstrakta datatyper och binära sökträd

Föreläsning 3 Datastrukturer (DAT037)

Föreläsning 8 i kursen Ma III, #IX1305, HT 07. (Fjärde föreläsningen av Bo Åhlander)

Tentamen Programmeringsteknik II Inledning. Anmälningskod:

Datastrukturer, algoritmer och programkonstruktion (DVA104, HT 2014) Föreläsning 5

Algoritmer, datastrukturer och komplexitet

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Datastrukturer. Föreläsning 5. Maps 1

Tentamen Datastrukturer D DAT 035/INN960 (med mycket kortfattade lösningsförslag)

Föreläsning 5: Dynamisk programmering

Föreläsning 5. Träd Binära träd Binärt sökträd som ADT Implementering av binärt sökträd Travestera binärt sökträd Sökning Insättning/borttagning

Föreläsning 5: Giriga algoritmer. Kruskals och Prims algoritmer

Föreläsning 7. Träd och binära sökträd

Föreläsning Datastrukturer (DAT037)

Tentamen TEN1 HI

Grafer MST Top. sortering Starkt samm. komponenter Kortaste avstånd. Grafalgoritmer 1. Douglas Wikström KTH Stockholm

Algoritmer och datastrukturer 2012, föreläsning 6

Föreläsning 5 TDDC91,TDDE22,725G97: DALG. Föreläsning i Datastrukturer och algoritmer 18 september 2018

Datastrukturer. föreläsning 8. Lecture 6 1

Föreläsning 4. Kö Implementerad med array Implementerad med länkad lista Djup kontra bredd Bredden först mha kö

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Tommy Färnqvist, IDA, Linköpings universitet

Föreläsning 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

Föreläsning 4 Innehåll. Abstrakta datatypen lista. Implementering av listor. Abstrakt datatypen lista. Abstrakt datatyp

Kapitel 7: Analys av sorteringsalgoritmer

Transkript:

Abstrakt datatyp för disjunkta mängder Vi skall presentera en abstrakt datatyp för att representera disjunkta mängder Kan bl.a. användas för att lösa ekvivalensproblemet avgör om två godtyckliga element hör till samma ekvivalensklass eller inte givet en relation R, avgör för två godtyckliga element om de är relaterade med varandra Tillämpningar används i flera grafalgoritmer, t.ex. för att bygga minimala spännträd automatteori kompilatorer 1 Ekvivalensrelationer En binär relation R på en mängd S är en delmängd av SxS (dvs. på en delmängd av den kartesiska produkten) relationen R är definierad för alla möjliga par av element i mängden S om a R b är sant säger vi att elementet a är relaterat till elementet b En ekvivalensrelation (RST-relation) uppfyller a R a, för alla a S (reflexiv) a R b om och endast om b R a (symmetrisk) a R b och b R c implicerar a R c (transitiv) 2 1

Exempel Relationen = (lika med) är en ekvivalensrelation reflexiv: a = a för alla a symmetrisk: a = b b = a transitiv: a = b och b = c a = c Relationen är inte en ekvivalensrelation reflexiv, eftersom a a transitiv eftersom a b och b c a c inte symmetrisk, eftersom a b medför inte att b a Relationen elektriskt kopplad till är en ekvivalensrelation reflexiv, eftersom a alltid är kopplad till sig själv symmetrisk: om a är kopplad till b så är b kopplad till a transitiv: om a är kopplad till b och b är kopplad till c så är a också kopplad till c 3 Dynamiska ekvivalensproblemet Givet en ekvivalensrelation, avgör för godtyckliga a och b om a b Vi kunde lagra information om vilka element är relaterade i en två-dimensionell tabell R[ ][ ] av logiska värden kan avgöra om två element a och b är relaterade i konstant tid finns lagrat i tabellen i R[a][b] Problemet med den här lösningen är att informationen om vilka element som är relaterade vanligtvis ges mera implicit en del av informationen om vilka element som är relaterade fås implicit som en följd av relationens reflexivitet, symmetri och transitivitet 4 2

Exempel Antag att vi har definierat en ekvivalensrelation på en mängd {a 1, a 2, a 3, a 4, a 5 } det finns då 25 par av element vilka antingen är relaterade med varandra eller inte Vi får information att följande element är relaterade: a 1 ~ a 2 a 3 ~ a 4 a 5 ~ a 1 a 4 ~ a 2 Eftersom är en ekvivalensrelation betyder det här att alla element är relaterade med varandra vi vill att vår datastruktur skall göra det möjligt att avgöra det här på ett effektivt sätt 5 Ekvivalensklasser En ekvivalensklass för ett element a S är den delmängd av S som innehåller alla element som är relaterade Ekvivalensklasserna bildar en partitionering av S varje element av S förekommer i exakt en ekvivalensklass För att avgöra om a b, behöver vi bara kontrollera om a och b finns i samma ekvivalensklass S: a d c e f b 6 3

Union/find algoritmen Inputen är först en samling av N mängder, alla med ett element varje element är bara relaterat med sig själv (reflexivitet) inga andra relationer finns mellan element mängderna är disjunkta, dvs. ett element hör till exakt en ekvivalensklass (S i S j = ) Det finns två tillåtna operationer find(a) returnerar namnet på mängden (dvs. ekvivalensklassen) som innehåller element a union(a,b) adderar relationer. Om vi vill lägga till relationen a b, checkar vi först om a och b redan är relaterade. dvs. om find(a) = find(b). Om inte gör vi en union som slår ihop de två ekvivalensklasserna som a och b hör till och bildar en ny ekvivalensklass Egenskaper hos union/find algoritmen Algoritmen är dynamisk mängderna förändras under algoritmen när vi gör unionoperationer Algoritmen opererar on-line den måste ge ett svar på en find-operation före den kan fortsätta Observera att vi inte utför några operationer som jämför elements relativa värden vi är bara intresserade av i vilken ekvivalensklass elementen finns Därför kan vi anta att alla element har numrerats sekventiellt från 0 till N-1 och att numreringen kan bestämmas lätt med någon hashing-metod 8 4

Egenskaper hos union/find algoritmen Initialt har vi alltså S i = {i} för i = 0 till N-1 Vår andra observation är att namnet på den mängd som returneras av find är ganska godtyckligt det enda som är viktigt är att find(a) = find(b) om och endast om a och b finns i samma mängd Det finns två strategier att lösa problemet den första garanterar att Find operationen kan utföras i konstant värsta-falls tid den andra garanterar att Union operationen kan utföras i konstant värsta-falls tid Man kan visa att båda inte kan utföras samtidigt i konstant värsta-falls tid 9 Find-operationen i konstant tid Vi lagrar namnet av ekvivalensklassen för varje element i en räcka då är find bara en enkel O(1) lookup 0 s 1 1 i räckan s 3 2 s 1 Antag att vi vill göra union(a,b) a hör till ekvivalensklass i och b hör till ekvivalensklass j vi måste gå igenom räckan och ändra i till j Union tar tiden θ(n) en följd av N-1 unioner (det maximala antalet) skulle alltså ta θ(n 2 ) tid 3 4 5 a b c d e f s 1 s 3 s 2 10 5

Länkade listor En idé är att hålla alla element som är i samma ekvivalensklass i en länkad lista sparar tid vid uppdatering, för vi s 1 behöver inte söka genom hela räckan s 3 Reducerar inte den asymptotiska körtiden det är fortfarande möjligt att utföra θ(n 2 ) ekvivalensklassuppdateringar under algoritmens lopp 0 1 2 3 4 5 a b c d e f s 1 s 1 s 3 s 2 11 Länkade listor med storlek Om vi också håller reda på storleken av varje ekvivalensklass, och när vi utför unioner ändrar namnet av den mindre klassen till den större, så är den totala tiden för N-1 sammanslagningar O(N log N) varje element kan få sin ekvivalensklass ändrad högst log N gånger varje gång en ekvivalensklass ändras så blir dess nya ekvivalensklass åtminstone dubbelt så stor En följd av M find-operationer och upp till N-1 unioner tar då högst O(M + N log N) tid 12 6

Union-operationen i konstant tid I resten av kapitlet skall vi undersöka en lösning till union/find problemet som gör unioner lätta (dvs. som kör i konstant tid) men find något svårare Körtiden för vilken som helst följd av högst M find-operationer och upp till N-1 unioner kommer bara att vara litet större än O(M + N) Algoritmerna som vi kommer att presentera är mycket effektiva är enkla och korta att implementera använder en enkel räcka för att lagra data är mycket svåra att analysera 13 Grundläggande datastruktur Find på två element returnerar samma svar om och endast om de finns i samma mängd vi kan använda ett träd för att representera varje mängd, eftersom varje element i ett träd har samma rot roten kan användas för att namnge mängden Ursprungligen innehåller varje mängd ett element vi har alltså en skog av N träd, vardera bestående bara av en enda rot-nod Då bara namnet av föräldern behövs kan vi lagra trädet implicit i en räcka s varje element s[i] representerar föräldern till element i om i är en rot, så sätter vi s[i] = -1 14

Exempel I skogen i figuren är s[i] = -1 för 0 i < 8 0 1 2 3 4 5 6 För att utföra en union av två mängder sammanslår vi de två träden genom att låta roten av ett träd peka på roten av det andra denna operation tar konstant tid vi antar konventionen att den nya roten efter union(x,y) är x Vi ritar träden explicit men underförstår att en räcka används för att lagra informationen 15 Exempel (forts.) Efter union(4,5) 0 1 2 3 4 6 Efter union(6,) 5 0 1 2 3 4 6 Efter union(4,6) 5 0 1 2 3 4 5 6 16 8

Exempel (forts.) Räckan som representerar träden efter de tre unionoperationerna ser nu ut på följande sätt: -1-1 -1-1 -1 4 4 6 0 1 2 3 4 5 6 Trädrepresentation: 0 1 2 3 4 5 6 1 Find-operationen En find(x) på element x utförs genom att returnera roten av trädet som innehåller x Tiden att utföra find är proportionell mot djupet av noden som representerar x förutsatt att vi kan hitta noden som representerar x i konstant tid Med strategin ovan kan man skapa ett träd av djupet N-1, så att värsta-falls körtiden för en find är O(N) M konsekutiva operationer skulle ta O(MN) tid i värsta fall 18 9

Union-operationen I vår implementation utförs Union alltid på rötterna av två träd Om man inte vill anta att elementen a och b är rötter måste man i Union-operationen göra find(a) och find(b) och utföra unionen på resultaten från dessa letar först fram rötterna till träden som a och b finns i, och utför sedan Union på dessa Genomsnittsanalysen för den presenterade algoritmen är riktigt svår 19 Implementation Klass-skelett och initieringsrutin för disjunkt mängd public class DisjSets { public DisjSet( int numelements ) public void union( int root1, int root2 ) public int find( int x ) private int [] s; } /* Initiera en disjunkt mängd */ public DisjSets(int numelements) { s = new int[numelements] for ( int i = 0; i < s.length; i++) s[i] = -1; } 20 10

Implementation (forts.) Union och Find rutinerna /* Antar att rot1 och rot2 är rötter */ public void union(int rot1, int rot2); { s[rot2] = rot1; /* Gör rot1 till förälder för rot2 */ } public int find(int x); { if( s[x] < 0 ) /* x är en rot, returnera den */ return x; else return find( s[x] ); /* annars gå ett steg uppåt */ } 21 Smarta Union-algoritmer I vår implementation av union(a,b) gjordes alltid det andra trädet (b) till ett subträd av det första (a) En enkel förbättring av algoritmen är att alltid göra det mindre trädet till ett subträd av det större ifall a och b är lika stora spelar det ingen roll vilket som blir ett subträd till det andra vi kan i så fall låta det andra trädet bli ett subträd till det första Metoden kallas union-by-size 22 11

Exempel på union-by-size Om vi gör union(3,4) med union-by-size blir resultatet: 0 1 2 4 3 5 6 Om vi inte hade använt union-by-size skulle resultatet ha blivit: 0 1 2 3 4 5 6 23 Körtid Vi kan bevisa att om unioner utförs med storlek, så är djupet av en nod aldrig mera än log N en nod är initialt på djupet 0 när dess djup ökar som ett resultat av en union, så placeras den i ett träd som är åtminstone dubbelt så stort som förut en nods djup kan alltså ökas högst log N gånger Detta visar att körtiden för en find operation är O(log N), och en följd av M operationer tar O(M log N) tid 24 12

Värsta fall Trädet i figuren visar det värsta träd som är möjligt efter 16 unioner fås om alla unioner är mellan träd av lika storlek 0 1 2 4 8 12 3 5 6 9 10 13 14 11 15 25 Storleken av ett träd För att implementera union-by-size måste vi hålla reda på storleken av varje träd Eftersom vi ju bara använder en räcka så kan vi sätta elementet av varje rot att innehålla det negativa värdet av storleken av dess träd Initialt består representationen av trädet av en räcka med bara -1:or när en union utförs kontrolleras storlekarna den nya storleken efter Union-operationen blir summan av de gamla Union-by-size fordrar alltså inget extra utrymme. 26 13

Implementation Implementation av union-by-size /* Antar att rot1 och rot2 är rötter */ public void union( int rot1, int rot2) { if ( s[rot2] <= s[rot1] ) /* rot2 är större eller lika */ { s[rot2] += s[rot1]; /* Addera storekarna */ s[rot1] = rot2 /* rot2 blir ny rot */ } else /* rot1 är det större trädet */ { s[rot1] += s[rot2]; s[rot2] = rot1; /* rot1 blir ny rot */ } } 2 Genomsnittlig körtid Det har visats att en följd av M operationer kräver O(M) genomsnittstid med union-by-size När slumpmässiga unioner utförs så sammanslås i allmänhet mycket små mängder (ofta bestående av ett element) med stora mängder trädens höjd ökar sällan sannolikheten för värsta-falls beteendet är mycket liten 28 14

Union-by-height En alternativ implementering som också garanterar att träden har djup högst O(log N) är union-by-height Vi håller reda på höjden (i st.f. storleken) av varje träd och utför unioner genom att göra det lägre trädet till ett subträd av det djupare trädet Höjden av ett träd ökar med ett när två lika djupa träd slås ihop när ett mindre träd slås ihop med ett större träd ändras höjden inte Eftersom ett träd bestående av en nod har höjden noll lagrar vi i roten trädets höjd minus ett 29 Exempel Vi visar ett träd och dess implicita representation både med union-by-size och union-by-height 0 1 2 4 Representation med union-by-size: 3 5 6-1 -1-1 4-5 4 4 6 0 1 2 3 4 5 6 Representation med union-by-height: -1-1 -1 4-3 4 4 6 0 1 2 3 4 5 6 30 15

Implementation Implementation av union-by-height find påverkas inte /* Antar att rot1 och rot2 är rötter */ public void union( int rot1, int rot2) { } if ( s[rot2] < s[rot1] ) /* rot2 är djupare */ else { } s[rot1] = rot2 /* rot2 blir ny rot */ if ( s[rot1] == s[rot2] ) /* Samma höjd */ s[rot1]--; /* Öka djupet med ett */ s[rot2] = rot1; /* rot1 blir ny rot */ 31 Ytterligare förbättringar Union/Find är för det mesta effektiv den är enkel och har i genomsnitt en linjär körtid för en följd av M operationer men värsta fallet med en körtid O(M log N) kan lätt inträffa, t.ex. om vi sätter alla mängder i en kö och tar ut de två första mängderna och sätter in unionen av dem För att snabba upp algoritmen undersöker vi find-operationen union-operationen kan inte mera förbättras 32 16

Stigkompression Stigkompression utförs under en find operation är oberoende av strategin för att utföra unioner I stigkompression, när vi utför find(x), så ändras varje nod på stigen från x till roten så att roten blir dess förälder Effekten av stigkompression är att med ett antal extra pekarändringar placeras noderna närmare roten iden är att detta kommer att snabba upp framtida accesser trädet blir lägre vi hoppas att det extra arbete som vi gör kommer att betala tillbaka sig i kommande find-operationer 33 Exempel Stigkompression efter find(14) på värsta-falls trädet Före: 0 1 2 4 8 3 5 6 Efter find(14) med stigkompression: 9 10 11 12 13 14 15 0 1 2 3 4 5 6 8 9 10 12 13 14 15 11 34 1

Implementation Enda förändring i koden är att s[x] sätts till värdet som returneras av find find(x) returnerar ju roten föräldern till x sätts till roten, rekursivt för alla noder uppåt på stigen till roten public int find(int x); { if( s[x] < 0 ) /* x är en rot */ return x; else return s[x] = find( s[x] ); /* Gå ett steg uppåt */ } /* och uppdatera föräldern */ 35 Stigkompression med union-by-height Stigkompression är inte helt kompatibelt med union-by-height, eftersom vi ju kan ändra trädens höjd att beräkna trädens höjd på nytt tar för mycket tid Lösningen är att låta bli att räkna om trädens höjd fastän de förändras av stigkompression värdena som lagras i roten ses som estimat av höjden kallas ibland trädets rank i stället för höjd Det har visats att union-by-rank är lika effektivt som union-by-size 36 18

Körtid Union/find med union-by-size och stigkompression har en nästan linjär värsta-falls körtid värsta-falls körtiden är Θ(M α(m,n)) om M N där α(m,n) är en funktionella invers av Ackermann s funktion, vilken definieras som A(1,j) = 2 j för j 1 A(i,1) = A(i-1, 2) för i 2 A(i,j) = A(i-1, A(i,j-1)) för i,j 2 Funktionen α(m,n) växer oerhört långsamt långsammare än log* N antalet gånger vi måste ta logaritmen av ett tal N förrän N 1 i praktiken är α(m,n) mindre än 5 för alla realistiska värden 31 Ett exempel: att generera labyrinter Union/find algoritmen kan användas för att generera labyrinter startpunkten är i övre vänstra hörnet slutpunkten är i nedre högra hörnet En enkel algoritm för att generera labyrinter: starta med n x m rektangulära celler med väggar runt varje cell, utom start- och slutpunkten välj slumpmässigt en inre vägg och ta bort den om de två celler som förenas av väggen inte redan är förbundna med varandra upprepa föregående steg tills alla celler är förbundna med varandra 0 1 2 3 4 5 6 8 9 10 11 12 13 14 15 16 1 18 20 19 21 22 23 24 38 19

Exempel: att generera labyrinter Ekvivalensrelationen som vi representerar är att två celler kan nås från varandra Initialt är alla celler åtskilda från varandra representeras av att alla celler hör till en egen disjunkt mängd Välj slumpmässigt en inre vägg ta bort väggen om de två cellerna på vardera sidan av väggen inte redan är nåbara från varandra använder find-operationen för att ta reda på om cellerna kan nås från varandra gör en union på de två cellerna för att markera att de är förbundna Upprepas till alla celler hör till samma mängd 0 1 2 3 4 5 6 8 9 10 11 12 13 14 15 16 1 18 ger en mera komplicerad labyrint än om vi slutar när slutcellen kan nås från startcellen 20 19 21 22 23 24 39 Exempellabyrint 40 20