• No results found

Optimeringar som gjorts innefattar till största del att koden har försökts hållas så ren och enkel som möjligt. En modern kompilator klarar att utföra de flesta typer av optimeringar såsom att ersätta multiplikation med shiftoperationer och att flytta ut onödig kod inuti loopar utanför loopar. Det som tjänas in på att t.ex. rulla ut en loop förloras i läsbarhet och i valet mellan läsbarhet och snabbhet har vi här valt läsbarhet.

Datastrukturer har valts med omsorg, t.ex. används Java.util.ArrayList för att lagra listan av hörn då operationer såsom size, isempty, get, set, iterator och listIterator har konstant tidskomplexitet och add har amorterad konstant tids-komplexitet. ArrayList har linjär tidskomplexitet för för att ta bort element under en iterering till skillnad från t.ex. Java.util.LinkedList som har konstant tidskom-plexitet för en sådan operation, men get används oftare och där har LinkedList linjär tidskomplexitet. Den konstanta termen är också högre i LinkedList vilket påverkar körhastigheten [7].

så sent som möjligt. Att undersöka om en punkt ligger i en polygon är en mycket kostsam operation (linjär tidskomlexitet) så detta görs helst så sällan som möjligt. Java Virtual Machine (JVM) kan optimera körningstiden ytterligare då den bl a har så kallad Java HotSpot vilket är en runtime optimerare som ser mönster i indata under körning och optimerar för vanlig indata. Anrop kan alltså få lägre körtid efter programmet kört ett tag i jämförelse med första anropet. För poly-goner med ett par tusen hörn handlar det om en sekund för det femte skelettet i ordningen som programmet räknar ut under samma körning. Effekten av denna typ av optimering är därmed marginell och inget som vi fördjupar oss i.

8 Resultat

Under utvecklandet av implementationen har många olika testfall använts, men de som används i körtidstestningen är en yttre kvadratisk polygon med hörn i origo och (10,10) och som innehåller en mängd hål som ligger på ett jämt avstånd från varandra. Hålen är 20-hörningar och liknar kvadrater där olika punkter på kvadratens kontur förflyttats lite åt vänster eller höger så att kvadraten blir "hac-kig"(se figur 17). Den typen av polygoner är bra för att testa slumpartade ”svåra” polygoner eftersom de innehåller väldigt många reflexiva hörn. Koordinaterna för polygonens hörn, vilket är vår indata, har tre värdesiffror.

Metoderna som använts för att mäta körtid är System.currentTimeMillis() för Java och gettimeofday() för C++. Dessa mäter förfluten tid från en referenstid-punkt till anropet, System.currentTimeMillis() i millisekunder och gettimeofday() i mikrosekunder. Tiden efter skelettet är framtaget subtraheras med tiden innan skelettanropet och för gettimeofday() omvandlas mikrosekunder till millisekunder. Dessa mäter alltså förfluten tid och kan ge osäkra resultat om andra processer kör i bakgrunden, men självklart har alla applikationer och åtkomst till nätverk och liknande stängts av innan körning.

Implementationen använde double som flyttalsrepresentation (se avsnitt 9.2).

Testerna kördes på en PC med en Intel i5 520m processor med 4GB RAM. Figur 18 visar resultatet av tidtagningen för denna implementation och för CGAL. Figur 19 visar även diagram över tidstestning för Felkel och Obdrzaleks [6] imple-mentation av deras algoritm. Det bör tilläggas att Felkel och Obdrzaleks artikel är författad 1998 och att testerna då körts på en dator som kan antas mycket mindre kraftfull än datorn som använts vid testerna av denna algoritm. Testerna kördes på konkava polygoner, men ur artikeln kan inte utläsas hur många reflexiva vinklar de innehöll. Denna är liksom CGAL implementerad i C++.

En jämförelse med CGAL Implementationen Polynomisk regression av implementationen CGAL Polynomisk regression av CGAL Antal hörn K ö rt id ( s) 0 1000 2000 3000 4000 5000 6000 120 100 80 60 40 20 0 (a)

Antal hål Antal hörn Implementationen (s) CGAL (s) 16 324 0,921 0,306 36 724 2,224 1,420 64 1284 6,295 5,134 100 2004 12,949 12,688 144 2884 25,366 28,541 196 3924 45,485 54,351 256 5124 75,112 99,299 (b)

Figur 19: Körtider för implementationen av FO-algoritmen. Inklippt från [6] med antal hörn på x-axeln och körtid i s på y-axeln.

Som synes i tabell 18b har vår implementation en initialt marginellt högre körtid än CGAL, men går om CGAL strax efter ett par tusen hörn, och har en körtid som är ca 25% snabbare än CGAL efter 5000 hörn. Vi ser direkt i grafen att körtiden inte är linjär i relation till antalet hörn hos polygonen, så vi låter Open Office utföra en polynomisk regression på våra datapunkter för vår implementation och för CGAL. Resultatet syns i diagram 18a och det visar sig att för vår implementation växer körtiden ungefärligen som n1,9 och för CGAL växer den ungefärligen som n2,1. Vår implementation uppvisar alltså O(n2) beteende såsom förväntat och båda körtiderna ser därmed ut att uppvisa en asymptotisk kvadratisk tillväxthastighet.

9 Diskussion

Som nämnt har vi övergett bignum-aritmetik och valt att använda flyttalsaritme-tik vid implementationen av denna algoritm. Detta för att få ner körningstiden. Implementationen av denna algoritm är tänkt att användas ute i industrin och skärhuvudet som skall följa vår skärbana har inte oändlig precision. Stålskivan som formerna ska utplaceras på är ungefär tre gånger tre meter stor och det är brukligt att den representeras av en fyrkant med hörn i (0, 0) och (10, 10) som är uppdelat i ett rutnät där rutorna är så stora som precisionen hos skärhuvudet.

Vet vi precisionen hos skärhuvudet kan vi räkna ut hur många värdesiffror vår indata till implementationen kommer bestå av. Om skärhuvudet har en precision på millimetern innebär det att vårt rutnät är 10, 000 × 10, 000 rutor stort och att vår indata har 4 värdesiffror. Beroende på skärhuvudets precision kommer koordinaterna till hörnen hos polygonen (som är indata till implementationen) beskrivas av ett visst antal värdesiffror som i sin tur kommer kräva ett visst antal värdesiffror i våra beräkningar för att resultatet skall bli korrekt.

Låt oss utreda korrektheten hos denna algoritm. Vi diskuterar först två problem som kan uppstå om vi inte använder oss av tillräckligt många värdesiffror i våra beräkningar för att sedan gå in på flyttalsaritmetik och val av flyttalsrepresentation för att säkerställa ett korrekt resultat.

9.1 Numerisk stabilitet hos algoritmen

Numerisk stabilitet, som innebär att små förändringar av indata till en algoritm ger liten påverkan på resultatet, är en önskvärd egenskap hos en algoritm. Tyvärr är denna algoritm inte numeriskt stabil i degenererande fall och dessa fall skall vi nu reda ut.

(a) Två kanthändelser, p1 och p2, uppfattas ske i samma punkt så P skapas med ingående kant kant2och utgående kant kant1.

kant1 kant2

h0

h1

h2

P

(b) Egentligen skedde p2i p1+  vil-ket inte resulterar i en samtida hän-delse i samma punkt. Först sker p1

och dess inåtgående kant blir kant2

och dess utgående kant blir h1h2.

kant1 kant2 h0 h1 h2 p1 Figur 20

9.1.1 Samtida händelse i samma punkt

Problematik uppstår när vi inte lyckas särskilja två punkter som ligger väldigt nära varandra. Händelsen förknippad med denna typ av problematik är samtida händelser i samma punkt. Vi uppfattar att två händelser, p1 och p2, sker samtidigt i samma punkt vilket resulterar i endast en led med en resulterande bisektris. Egentligen skedde dock p2 i p1+  vilket hade resulterat i att p1 och p2 inte skedde i samma punkt och att två separata leder hade skapats med en bisektris vardera vilket hade gett ett helt annorlunda utseende till det rakkantiga skelettet (se figur 20).

9.1.2 Ej upptäckta skärningspunkter

En annan problematik uppstår när vi inte lyckas särskilja lutningarna hos två bisektriser där bisektriserna är nästintill parallella. Vi tänker oss två intilliggande hörn h1 och h2. Vi uppfattar hörnens bisektrisers lutningar, k1 och k2, som lika och eftersom två parallella linjer ej skär varandra så beräknas en kanthändelse mellan h1 och h2 vara omöjlig. Om k2 egentligen var k1 +  så var bisektriserna inte parallella och en kanthändelse mellan h1 och h2 hade vart möjlig vilket hade resulterat i ett helt annat utseende på hela det rakkantiga skelettet.

Flyttalsrepresentation Bitar Maximal exponent Antal värdesiffror i mantissan (2-potens) (10-potens)

Float 32 1038 23 7

Double 64 10307 52 15

Quadruple 128 104931 112 34

Figur 21: Olika typer av flyttalsrepresentationer.

cision. För att inte råka ut för situationer liknande de som beskrivits i detta kapitel måste vi kunna skilja på alla tal så att vi inte uppfattar två tal som inte är lika som lika eller tvärtom.

Related documents