Konvexa höljet Laboration 6 — GruDat, DD1344 Örjan Ekeberg 10 december 2008 Målsättning Denna laboration ska ge dig övning i att implementera en algoritm utgående från en beskrivning av algoritmen. Du ska få insikt i hur man kan manipulera geometriska objekt som punkter, linjer och polygoner. Du kommer också att få prova på att använda enkel grafik för att visualisera resultatet av din algoritm. 1 Uppgiften Du ska skriva ett program som räknar ut det konvexa höljet för en punktmängd. Punkterna läser du in från filen sweden.txt där varje rad innehåller två tal som ska tolkas som x- och y-koordinater. Det konvexa höljet är den minsta konvexa polygonen där ingen punkt ligger utanför. Figur 1 visar ett exempel. Det konvexa höljet till en mängd punkter i planet kan beräknas med en algoritm som kallas Graham-Scan. Din uppgift är att implementera denna algoritm och använda den för att hitta och rita ut det konvexa höljet till punkterna i filen. Figur 1: Konvexa höljet till en punktmängd i planet. Det konvexa höljet är en polygon som byggs upp av en delmängd av punkterna. 1 2 Algoritmen Graham-Scan går till på följande sätt: 1. Låt p0 vara den punkt som har minst x-koordinat. Om fler har samma x-koordinat, välj den (av dessa) som har störst ykoordinat. 2. Låt [p1 , p2 , . . . , pn ] vara resten av punkterna, ordnade motsols runt punkten p0 . 3. Lägg in punkterna p0 , p1 och p2 i en stack s. 4. För varje punkt p i {p3 , p4 , . . . , pn } • Så länge punkten överst på s ligger till vänster om linjen från punkten näst överst på s till p så ta bort stackens översta element. • Lägg in p på stacken. 5. Returnera punkterna i s Steg 1 garanterar att punkten p0 är en punkt som kommer att tillhöra lösningen. Steg 2 kräver att punkterna sorteras efter stigande vinkel för linjen från p0 till den aktuella punkten (se figur 2). Huvuddelen av algoritmen är loopen i steg 4. Hur den fungerar illustreras i figur 3. p7 p6 p5 p4 p0 p2 p3 p1 Figur 2: Punkterna p1 , p2 , . . . , pn sorteras efter stigade vinkel på linjen som utgår från p0 . 2 p7 A p6 p0 p5 p2 p6 p4 p0 p3 p1 p7 p6 p2 p4 p3 p7 D p5 p2 p6 p4 p0 p3 p1 p5 p2 p4 p3 p1 p7 E p6 p0 p5 p1 C p0 p7 B p2 p7 F p5 p6 p4 p0 p3 p1 p2 p5 p4 p3 p1 Figur 3: Bilderna illustrerar hur Graham-Scan algoritmen succesivt bygger polygonen. Först (A) upptäcker man att punkten p2 ligger till vänster om linjen från p1 till p3 . p2 plockas därför bort från stacken innan p3 läggs in. I nästa steg (B) konstateras att p3 ligger till höger om linjen p1 → p4 . p3 lämnas därför kvar på stacken när p4 läggs in. I steg C jämförs p4 med linjen p3 → p5 och eftersom punkten ligger till höger om linjen så lämnas den kvar på stacken. I D jämförs p5 med linjen p4 → p6 och även här ligger punkten till höger om linjen. I nästa steg (E) plockas först punkten p6 bort eftersom den ligger till vänster om p5 → p7 , men här plockas även p5 bort eftersom den ligger till vänster om p4 → p7 . Den slutliga lösningen utgörs av punkterna som finns kvar på stacken när alla punkter behandlats (F). 3 3 Hjälpfunktioner Graham-Scan använder sig av en återkommande test: befinner sig en punkt p till vänster om en linje p0 → p1 ? Detta undersöker man enklast (och säkrast) genom att beräkna kryssprodukten av vektorerna p0 → p och p0 → p1 och avgöra om denna är positiv eller ej. Kryssprodukten mellan två vektorer a och b i planet räknas enkelt ut som ax by − ay bx . Skriv en hjälpfunktion som tar tre punkter och returnerar True om den första punkten ligger till vänster om linjen från den andra till den tredje. För att kunna sortera punkterna i vinkelordning behöver man räkna ut vinkeln för en vektor. Här är det lämpligt att utnyttja funktionen math.atan2(y, x) som ger vinkeln (i radianer) från origo till punkten (x, y). För att sorteringen ska fungera är det viktigt att vinkelberäkningarna inte plötsligt gör ett hopp på 2π. Genom att utgå från punkten med minst x-koordinat är vi garanterade att alla vinklar ligger mellan − π2 och π2 . math.atan2 gör sitt “hopp” vid π så detta bör vara säkert. 4 Implementera Graham-Scan Implementera nu de funktioner som behövs för att räkna ut vilken polygon som utgör det konvexa höljet till punkterna i filen sweden.txt. För sorteringen kan du med fördel utnyttja den inbyggda funktionen sorted. Lägg märke till att sorted kan ta en extraparameter (en s.k. nyckelordsparameter) i form av en funktion som plockar fram den nyckel man vill sortera efter; i vårt fall vinkeln. Sorteringen kan därför göras på detta sätt: def myKey( p o i n t ) : return angleBetween ( o r i g i n , p o i n t ) origin = p [0] v = s o r t e d ( p [ 1 : ] , key=myKey) 4 5 Sköldpaddsgrafik Under 1960-talet utvecklades ett experimentellt programmeringsspråk Logo speciellt för att lära barn programmering. Logo introducerade en speciell sorts grafik kallad Turtle Graphics, där namnet syftar på den typ av figurer som skapas när sköldpaddor går omkring på en sandstrand. Python har en modul turtle som implementerar denna typ av grafik. Prova gärna följande: import t u r t l e turtle turtle turtle turtle turtle turtle turtle turtle turtle . down ( ) . goto (100 , 0) . up ( ) . goto (50 , 0) . down ( ) . c i r c l e (10) . c o l o r ( ’ blue ’ ) . right (90) . forward (20) Här är ett urval av de funktioner som finns: down up goto right forward circle color speed tracer Sänk pennan Lyft pennan Gå till angivna koordinater Sväng höger Gå framåt Gå runt Byt färg på pennan Trimma sköldpaddan Animerad uppritning på/av Mer information får du genom help(turtle) i Python, bl.a. hur du fyller ytor och skriver text. Använd sköldpaddsgrafik för att rita ut alla punkterna och det konvexa höljet. Använd t.ex. små cirklar för punkterna och linjer för det konvexa höljet. Rita gärna lite större cirklar för de punkter som ingår i höljet. Sköldpaddsgrafiken är från början inställd att rita långsamt för att man ska hinna se vad som händer. Om du tycker att tempot är lite väl lågt kan du öka sköldpaddans fart genom turtle .speed(’ fastest ’ ). Alternativt kan du helt stänga av animeringen under uppritningen genom turtle . tracer (False). 5 6 6.1 Extrauppgift för högre betyg Största avståndet När man har det konvexa höljet kan man använda detta för att snabbt (O(n)) räkna ut det största avståndet mellan två punkter i den ursprungliga punktmängden. En brute force algoritm som beräknar alla parvisa avstånd kommer att kräva O(n2 ) tid. För att komma ned till O(n) måste man vara smartare. En idé är att stega igenom polygonen med en variabel medan man samtidigt i en annan variabel håller reda på vilken punkt i polygonen som ligger längst bort. Använd denna idé för att skriva en funktion som räknar ut vilka två punkter som ligger längst ifrån varandra. Rita en linje mellan dessa punkter (gärna med en annan färg). När du är klar med laborationen: redovisa för en handledare och försök att få en kvittens här. Ditt namn: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Handledarens signatur: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dagens datum: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Extrauppgift genomförd: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . GruDat 2008, Laboration 6 6