TIE-20100 Tietorakenteet ja algoritmit, kevät 2019 Harjoitustyö 2: Game of Fibres Viimeksi päivitetty 08.04.2019 Tapahtui edellisessä harjoitustyössä... Tämä harjoitustyö laajentaa suoraan harjoitustyö 1:tä (Beacons of RGB). Kaikki edellisessä työohjeessa olleet asiat pätevät edelleen, tässä työohjeessa mainitaan vain uudet, tässä harjoitustyössä toteutettavat asiat ja niiden vaatimukset, tai asiat jotka ovat muuttuneet edellisestä työohjeesta. Harjoitustyön aihe Aikojen kuluessa Cmykin sivilisaatio on kehittynyt, ja sen insinöörit ovat huomanneet että maata vaivaavan jatkuvan sumun takia majakoiden valo saadaan välitettyä majakalta toiselle tehokkaammin maan alla kulkevia valokuituja pitkin. Valokuidut ("fibre" harjoitustyön koodissa) muodostavat risteävän verkoston, jossa risteyksessä ("xpoint" coodissa) voidaan valita mihin toiseen kuituun tuleva valonsäde välitetään. Jos majakka sijaitsee kuitujen risteyksessä (tai yksittäisen kuidun päässä), se voi lähettää ja vastaanottaa valoa kuituja pitkin. Tässä harjoitustyössä ensimmäisen harjoitustyön luokkaa laajennetaan käsittelemään kuituverkostoa ja etsimään sieltä valolle sopivia reittejä. Harjoitustyössä harjoitellaan edelleen valmiiden tietorakenteiden ja algoritmien tehokasta käyttöä (STL), mutta siinä harjoitellaan myös algoritmien tehokasta toteuttamista ja niiden tehokkuuden arvioimista. Jokaiselle valokuidulla on kaksi päätepistettä, joiden koordinaatit annetaan. Valo voi kulkea kuidussa kumpaan suuntaan tahansa. Lisäksi jokainen kuitu hidastaa valoa tietyn määrän (fysiikasta muistamme, että valon nopeus väliaineessa vaihtelee), joten jokaiselle kuidulle annetaan myös "kustannus" ("cost" koodissa), joka kertoo valon kulkuajan kuidussa. Ohjelmalla voi lisätä ja poistaa kuituja, kysyä tietysti koordinaatista lähteviä kuituja, etsiä tietyillä kriteereillä valolle reittejä kuituja pitkin, ja ei-pakollisena osana optimoida kuituverkostoa. Koska kyseessä on Tietorakenteiden ja algoritmien harjoitustyö, ohjelman tehokkuus on edelleen tärkeä arvostelukriteeri. Tavoitteena on tehdä mahdollisimman tehokas toteutus, kun oletetaan että kaikki ohjelman tuntemat komennot ovat suunnilleen yhtä yleisiä (ellei komentotaulukossa toisin mainita). Plussaa tietysti saa, mitä tehokkaammin operaatiot pystyy toteuttamaan. Tämän harjoitustyön uusissa operaatioissa asymptoottiseen tehokkuuteen ei välttämättä pysty hirveästi vaikuttamaan, koska käytetyt algoritmit määräävät sen. Sen vuoksi harjoitustyössä algoritmien toteutukseen ja toiminnallisuuteen kiinnitetään enemmän huomiota kuin tehokkuuteen.
Huomaa erityisesti seuraavat asiat (osa uusia, osa toistettu tärkeyden vuoksi): Osana ohjelman palautusta tiedostoon datastructures.hh on jokaisen operaation oheen laitettu kommentti, johon lisätään oma arvio kunkin toteutetun operaation asymptoottisesta tehokkuudesta lyhyiden perusteluiden kera. Osana ohjelman palautusta palautetaan git:ssä myös dokumentti (samassa hakemistossa/kansiossa kuin lähdekoodi), jossa perustellaan toteutuksessa käytetyt tietorakenteet tehokkuuden kannalta. Hyväksyttäviä dokumentin formaatteja ovat puhdas teksti (readme.txt), markdown (readme.md) ja Pdf (readme.pdf). Operaatioiden route_least_xpoints(), route_fastest(), route_fibre_cycle() ja trim_fibre_network() toteuttaminen ei ole pakollista läpipääsyn kannalta. Ne ovat kuitenkin osa arvostelua. Osa ei-pakollisista operaatioista saattaa vaatia algoritmeja, joita ei ole käyty läpi luennolla (mutta kyllä mahdollisesti muualla kurssilla). Riittävän huonolla toteutuksella työ voidaan hylätä. Järjestämisestä Jos operaation paluuarvona vaaditaan lista järjestettyjä koordinaatteja, järjestämisessä tulee käyttää koordinaateille valmiiksi määriteltyä "<"-vertailua. Kuitureittien tulostamisesta Harjoitustyössä operaatiot palauttavat usein reitin kuituja pitkin kahden pisteen välillä. Kooditasolla tämä tapahtuu palauttamalla lista pareja, joissa on ensimmäisenä koordinaatti ja toisena reitin kustannus ko. koordinaattiin saakka. Ensimmäisenä on reitin lähtöpiste (kustannuksena 0), sitten reitin seuraava risteyspiste jne., ja päämäärä viimeisenä (kustannuksena koko reitin kustannus). Ohjelman toiminta ja rakenne Osa ohjelmasta tulee valmiina kurssin puolesta, osa toteutetaan itse. Graafisen käyttöliittymän käytöstä QtCreatorilla käännettäessä harjoitustyön valmis koodi tarjoaa graafisen käyttöliittymän, jolla ohjelmaa voi testata ja ajaa valmiita testejä sekä visualisoida ohjelman toimintaa. Uusina ominaisuuksina käyttöliittymässä voi nyt valita kuitujen näyttämisen graafisessa näkymässä ja sen, tuottaako näkymän klikkaaminen hiirellä majakan ID:n (kuten 1. työssä) vai kuituristeyksen koordinaatit (vai risteyskohtia voi klikata). Lisäksi käyttöliittymässä on valinta "Beam routes". Jos tämä on valittuna, yritetään graafisessa näkymässä majakoiden valonsäteet reitittää kuituja pitkin (käyttäen pakollista operaatiota route_any(). Jos reittiä ei operaatiolla löydy, näytetään valonsäde edelleen suoraan majakasta toiseen (siis ilmateitse). (Jos samaa kuitua pitkin kulkee useita valonsäteitä, näytetään niistä vain yksi.)
Huom! Käyttöliittymän graafinen esitys kysyy kaikki tiedot opiskelijoiden koodista! Se ei siis ole "oikea" lopputulos vaan graafinen esitys siitä, mitä tietoja opiskelijoiden koodi antaa. Käyttöliittymä hakee kaikki majakat operaatiolla all_beacons() ja kysyy majakoiden tiedot operaatioilla get_...(). Jos valonsäteiden piirtäminen on päällä, ne kysytään operaatiolla get_lightsources(), ja jos säteiden väritys on päällä, säteiden värit kysytään operaatiolla total_color() (vapaaehtoinen). Jos kuitujen piirto on päällä, ne kysytään operaatiolla all_xpoints() ja get_fibres_from(). (Ja kuten edellä todettiin, valonsäteiden reititys tehdään operaatiolla route_any()). Harjoitustyönä toteutettavat osat Tiedostot datastructures.hh ja datastructures.cc class Datastructures: Luokan julkisen rajapinnan jäsenfunktiot tulee toteuttaa. Luokkaan saa listätä omia määrittelyitä (jäsenmuuttujat, uudet jäsenfunktiot yms.) Tiedostoon datastructures.hh kirjoitetaan jokaisen toteutetun operaation yläpuolelle kommentteihin oma arvio ko. operaation toteutuksen asymptoottisesti tehokkuudesta ja lyhyt perustelu arviolle. Lisäksi harjoitustyönä toteutetaan alussa mainittu dokumentti readme.pdf. Huom! Mahdolliset Debug-tulostukset kannattaa tehdä cerr-virtaan (tai qdebug:lla, jos käytät Qt:ta), jotta ne eivät sotke testejä. Ohjelman tuntemat komennot ja luokan julkinen rajapinta Kun ohjelma käynnistetään, se jää odottamaan komentoja, jotka on selitetty alla. Komennot, joiden yhteydessä mainitaan jäsenfunktio, kutsuvat ko. Datastructure-luokan operaatioita, jotka siis opiskelijat toteuttavat. Osa komennoista on taas toteutettu kokonaan kurssin puolesta pääohjelmassa. Alla luetellaan vain tämän harjoitustyön uudet komennot, myös kaikki harjoitustyön 1 komennot ovat käytettävissä. Jos ohjelmalle antaa komentoriviltä tiedoston parametriksi, se lukee komennot ko. tiedostosta ja lopettaa sen jälkeen. Alla operaatiot on listattu siinä järjestyksessä, kun ne suositellaan toteutettavaksi (tietysti suunnittelu kannattaa tehdä kaikki operaatiot huomioon ottaen jo alun alkaen). Komento Julkinen jäsenfunktio all_xpoints std::vector<coord> all_xpoints() Selitys Palauttaa kaikki tietorakenteessa olevat kuitujen päätepisteet koordinaattien mukaisessa järjestyksessä (ks. Järjestämisestä aiemmin), ja jokainen päätepiste on mukana vain kerran. Tämä operaatio ei ole oletuksena mukana tehokkuustesteissä.
Komento Julkinen jäsenfunktio add_fibre (x1,y1) (x2,y2) cost bool add_fibre(coord xpoint1, Coord xpoint2, Cost cost) fibres (x,y) std::vector<std::pair<coord, Cost>> get_fibres_from(coord xy) all_fibres std::vector<std::pair<coord, Coord>> all_fibres() remove_fibre (x1,y1) (x2,y2) bool remove_fibre(coord xpoint1, Coord xpoint2) clear_fibres void clear_fibres() (Allaolevat kannattaa toteuttaa todennäköisesti vasta, kun ylläolevat on toteutettu.) route_any (x1,y1) (x2,y2) std::vector<std::pair<coord, Cost>> route_any(coord fromxy, Coord toxy) (Seuraavien operaatioiden toteuttaminen ei ole pakollista, mutta ne parantavat arvosanaa.) route_least_xpoints (x1,y1) (x2,y2) std::vector<std::pair<coord, Cost>> route_least_xpoints(coord fromxy, Coord toxy) Selitys Lisää tietorakenteeseen uuden valokuidun annettujen pisteiden välille ja annetulla hinnalla. Jos annettujen pisteiden välillä on jo kuitu tai jos molemmat pisteet ovat samat, ei tehdä mitään ja palautetaan false, muuten palautetaan true. Palauttaa listan koordinaatteja, joihin annetusta koordinaatista menee suoraan kuituja, ja ko. kuidun hinnan. Lista on järjestetty koordinaattien mukaiseen järjestykseen. Jos annetusta pisteestä ei lähde kuituja, palautetaan tyhjä lista. Palauttaa listan kaikista kuiduista. Listassa jokainen kuitu ilmoitetaan sen päätepisteiden muodostamana koordinaattiparina niin, että lista on järjestetty ensisijaisesti ensimmäisen koordinaatin mukaan, toissijaisesti toisen. Kukin kuitu on listassa vain kertaalleen niin, että sen 1. koordinaatti on pienempi kuin 2. koordinaatti. Poistaa kuidun annettujen koordinaattien väliltä. Jos koordinaattien välillä ei ollut kuitua, palautetaan false, muuten true. Tyhjentää kuituverkoston eli poistaa kaikki kuidut. Huom! Kaikki majakat ja niiden valonsäteet säilyvät edelleen. Palauttaa jonkin (mielivaltaisen) reitin annettujen pisteiden välillä (ks. "Kulkureittien tulostamisesta"). Palautetussa vektorissa on ensimmäisenä alkupiste hinnalla 0, sitten kaikki reitin varrella olevat pisteet ja kustannus ko. pisteeseen saakka, viimeisenä loppupiste. Jos reittiä ei löydy, palautetaan tyhjä vektori. Palauttaa annettujen pisteiden välillä reitin (ks. "Kulkureittien tulostamisesta"), jossa on mahdollisimman vähän risteyksiä (ja siis myös mahdollisimman vähän kuituja). Palautetussa vektorissa on ensimmäisenä alkupiste hinnalla 0, sitten kaikki reitin varrella olevat pisteet ja kustannus ko. pisteeseen saakka, viimeisenä loppupiste. Jos reittiä ei löydy, palautetaan tyhjä vektori.
Komento Julkinen jäsenfunktio route_fastest (x1,y1) (x2,y2) std::vector<std::pair<coord, Cost>> route_fastest(coord fromxy, Coord toxy) route_fibre_cycle (x1,y1) std::vector<coord> route_fibre_cycle(coord startxy) trim_fibre_network Cost trim_fibre_network() (Seuraavat komennot on toteutettu valmiiksi pääohjelmassa.) random_fibres n (pääohjelman toteuttama) random_labyrinth xsize ysize extra_routes (pääohjelman toteuttama) Selitys Palauttaa annettujen pisteiden välillä reitin (ks. "Kulkureittien tulostamisesta"), joka on mahdollisimman nopea, ts. jonka kustannus on mahdollisimman pieni. Palautetussa vektorissa on ensimmäisenä alkupiste hinnalla 0, sitten kaikki reitin varrella olevat pisteet ja kustannus ko. pisteeseen saakka, viimeisenä loppupiste. Jos reittiä ei löydy, palautetaan tyhjä vektori. Tarkastaa, voiko annetusta pisteestä kulkea kuituverkkoa pitkin niin, että reittiin muodostuu silmukka (palataan eri kuitua pitkin johonkin reitin varrella olevaan pisteeseen). Paluuarvona palautetaan reitti, joka päättyy ko. silmukkaan (paluuarvon viimeinen piste on se, johon palaaminen muodostaa silmukan). Jos reittiä ei löydy, palautetaan tyhjä vektori. Huom, tulostuksen yhtenäistämiseksi pääohjelma tulostaa palautetusta reitistä vain silmukan, ja vielä niin, että silmukka kierretään haarautumiskohdasta pienemmän koordinaatin suuntaan. Graafisesti reitti näytetään kokonaisuudessaan. Jättää jäljelle kuidut, joiden yhteenlaskettu kustannus on mahdollisimman pieni, mutta joilla edelleen löytyy reitti sellaisten pisteiden välillä, joiden välillä oli ennenkin reitti. Muut kuidut poistetaan. Paluuarvona palautetaan tuloksena olevan kuituverkoston kokonaiskustannus. Jos olemassa useita kokonaiskustannukseltaan yhtä lyhyitä kuituverkostoja, mikä tahansa niistä kelpaa. Lisää maksimissaan n satunnaista valokuitua majakoiden välille niin, että arvotut kuidut eivät risteä keskenään (käyttöliittymän selkeyttämiseksi). Kuituja saatetaan lisätä myös vähemmän. Tehokkuustestissä tämä komento lisää aina maks. 10 kuitua. Lisää satunnaisen kuitulabyrintin, jossa on ysize riviä, joissa on xsize risteystä. Extra_routes kertoo, kuinka monta "ylimääräistä" kuitua korkeintaan lisätään. Jos extra_routes on 0, pääsee labyrintissä joka pisteestä tasan yhtä reittiä toiseen pisteeseen.
Komento Julkinen jäsenfunktio perftest all/compulsory/cmd1;cmd2... timeout n n1;n2;n3... (pääohjelman toteuttama) Selitys Ajaa ohjelmalle tehokkuustestit. Tyhjentää tietorakenteen ja lisää sinne n1 kpl satunnaisia majakoita (ks. random_add) ja näiden välille satunnaisen määrän kuituja. Sen jälkeen arpoo n kertaa satunnaisen komennon. Mittaa ja tulostaa sekä lisäämiseen että komentoihin menneen ajan. Sen jälkeen sama toistetaan n2:lle jne. Jos jonkin testikierroksen suoritusaika ylittää timeout sekuntia, keskeytetään testien ajaminen (tämä ei välttämättä ole mikään ongelma, vaan mielivaltainen aikaraja). Jos ensimmäinen parametri on all, arvotaan lisäyksen jälkeen kaikista komennoista, joita on ilmoitettu kutsuttavan usein. Jos se on compulsory, testataan vain komentoja, jotka on pakko toteuttaa. Jos parametri on lista komentoja, arvotaan komento näiden joukosta (tällöin kannattaa mukaan ottaa myös random_add, jotta lisäyksiä tulee myös testikierroksen aikana). Jos ohjelmaa ajaa graafisella käyttöliittymällä, "stop test" nappia painamalla testi keskeytetään (nappiin reagointi voi kestää hetken). Huom! 2. harjoitustyössä perftest ei oletuksena enää testaa 1. työn aakkostamiseen ja valonsäteisiin liittyviä operaatioita, ainoastaan majakoiden lisäämisiä, erityisen usein tehtäviä operaatioita ja kuituihin liittyviä uusia operaatioita (lisäämisvaiheessa lisätään edelleen satunnaisia valonsäteitä). "Datatiedostot" Kätevin tapa testata ohjelmaa on luoda "datatiedostoja", jotka add-komennolla lisäävät joukon majakoita ohjelmaan. Majakat voi sitten kätevästi lukea sisään tiedostosta read-komennolla ja sitten kokeilla muita komentoja ilman, että majakat täytyisi joka kerta syöttää sisään käsin. Alla on esimerkit datatiedostoista, joista toinen lisää majakoita, toinen valonsäteitä: example-beacons.txt (päivitetty vastaamaan tiedoston sisältöä 8.4.) # Add beacons add_beacon G1 Lime (0,0) (0,255,0) add_beacon M1 Fuchsia (6,0) (255,0,255) add_beacon R1 Crimson (1,6) (220,20,60) add_beacon B2 Teal (11, 10) (0,128,128) add_beacon M2 Indigo (10, 4) (75,0,130)
example-lightbeams.txt # Add light sources add_lightbeam G1 M1 add_lightbeam R1 M2 add_lightbeam M1 M2 add_lightbeam M2 B2 example-fibres.txt add_fibre (0,0) (6,0) 1 add_fibre (6,0) (10,4) 1 add_fibre (10,4) (6,6) 1 add_fibre (6,6) (11,10) 1 add_fibre (0,0) (1,6) 2 add_fibre (1,6) (6,6) 3 Esimerkki ohjelman toiminnasta Alla on esimerkki ohjelman toiminnasta. Esimerkin syötteet löytyvät tiedostoista examplecompulsory-in.txt ja example-all-in.txt, tulostukset tiedostoista example-compulsory-out.txt ja example-all-out.txt. Eli esimerkkiä voi käyttää pienenä testinä pakollisten toimintojen toimimisesta antamalla käyttöliittymästä komennon testread "example-compulsory-in.txt" "example-compulsory-out.txt" > read "example-compulsory-in.txt" ** Commands from 'example-compulsory-in.txt' > clear_beacons Cleared all beacons > clear_fibres All fibres removed. > read "example-beacons.txt" ** Commands from 'example-beacons.txt' > # Add beacons > add_beacon G1 Lime (0,0) (0,255,0) Lime: pos=(0,0), color=(0,255,0):1530, id=g1 > add_beacon M1 Fuchsia (6,0) (255,0,255) Fuchsia: pos=(6,0), color=(255,0,255):1020, id=m1 > add_beacon R1 Crimson (1,6) (220,20,60) Crimson: pos=(1,6), color=(220,20,60):840, id=r1 > add_beacon B2 Teal (11, 10) (0,128,128) Teal: pos=(11,10), color=(0,128,128):896, id=b2 > add_beacon M2 Indigo (10, 4) (75,0,130) Indigo: pos=(10,4), color=(75,0,130):355, id=m2 > ** End of commands from 'example-beacons.txt' > read "example-lightbeams.txt" ** Commands from 'example-lightbeams.txt' > # Add light sources > add_lightbeam G1 M1 Added lightbeam: Lime -> Fuchsia > add_lightbeam R1 M2 Added lightbeam: Crimson -> Indigo > add_lightbeam M1 M2 Added lightbeam: Fuchsia -> Indigo > add_lightbeam M2 B2 Added lightbeam: Indigo -> Teal > ** End of commands from 'example-lightbeams.txt'
> read "example-fibres.txt" ** Commands from 'example-fibres.txt' > add_fibre (0,0) (6,0) 1 Added fibre: (0,0) <-> (6,0), cost 1 > add_fibre (6,0) (10,4) 1 Added fibre: (6,0) <-> (10,4), cost 1 > add_fibre (10,4) (6,6) 1 Added fibre: (10,4) <-> (6,6), cost 1 > add_fibre (6,6) (11,10) 1 Added fibre: (6,6) <-> (11,10), cost 1 > add_fibre (0,0) (1,6) 2 Added fibre: (0,0) <-> (1,6), cost 2 > add_fibre (1,6) (6,6) 3 Added fibre: (1,6) <-> (6,6), cost 3 > ** End of commands from 'example-fibres.txt' > all_xpoints 1. (0,0) 2. (6,0) 3. (10,4) 4. (1,6) 5. (6,6) 6. (11,10) > fibres (6,6) 1. (10,4) : 1 2. (1,6) : 3 3. (11,10) : 1 > remove_fibre (6,6) (1,6) Removed fibre: (6,6) <-> (1,6) > fibres (6,6) 1. (10,4) : 1 2. (11,10) : 1 > route_any (0,0) (11,10) 0. (0,0) : 0 1. -> (6,0) : 1 2. -> (10,4) : 2 3. -> (6,6) : 3 4. -> (11,10) : 4 > clear_fibres All fibres removed. > all_xpoints > ** End of commands from 'example-compulsory-in.txt' > read "example-non-compulsory-in.txt" ** Commands from 'example-non-compulsory-in.txt' > clear_beacons Cleared all beacons > clear_fibres All fibres removed. > read "example-beacons.txt" ** Commands from 'example-beacons.txt' ** End of commands from 'example-beacons.txt' > read "example-lightbeams.txt" ** Commands from 'example-lightbeams.txt' ** End of commands from 'example-lightbeams.txt' > read "example-fibres.txt"
** Commands from 'example-fibres.txt' ** End of commands from 'example-fibres.txt' > route_least_xpoints (0,0) (11,10) 0. (0,0) : 0 1. -> (1,6) : 2 2. -> (6,6) : 5 3. -> (11,10) : 6 > route_fastest (0,0) (11,10) 0. (0,0) : 0 1. -> (6,0) : 1 2. -> (10,4) : 2 3. -> (6,6) : 3 4. -> (11,10) : 4 > route_fibre_cycle (11,10) 0. (6,6) 1. -> (10,4) 2. -> (6,0) 3. -> (0,0) 4. -> (1,6) 5. -> (6,6) > all_fibres (0,0) -> (6,0) (0,0) -> (1,6) (6,0) -> (10,4) (10,4) -> (6,6) (1,6) -> (6,6) (6,6) -> (11,10) > trim_fibre_network The remaining fibre network has total cost of 6 > all_fibres (0,0) -> (6,0) (0,0) -> (1,6) (6,0) -> (10,4) (10,4) -> (6,6) (6,6) -> (11,10) > ** End of commands from 'example-non-compulsory-in.txt' > quit Kuvakaappaus käyttöliittymästä Alla vielä kuvakaappaus käyttöliittymästä sen jälkeen, kun example-beacons.txt ja examplelightbeams.txt ja example-fibres.txt on luettu sisään.