Konvexa höljet Laboration 6 — GruDat, DD1344

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