TIE-20100 Tietorakenteet ja algoritmit, kevät 2017 Versio 30.1.2017 Harjoitustyö 1: Palkkatilastot Harjoitustyön aihe Tässä harjoitustyössä tehdään vuoden vaihtumisen kunniaksi ohjelma, jolla saa tuotettua informaatiota ihmisten palkoista. Ohjelmalle annetaan tietoa työntekijöistä (nimi ja palkka), ja siltä saa kysyttyä työntekijälistan nimi- tai palkkajärjestyksessä. Lisäksi ohjelmalla saa haettua palkkoihin liittyviä tunnuslukuja, joita ihan oikeastikin käytetään: minimi- ja maksimipalkan, mediaanipalkan sekä palkkakvartiilit (tunnuslukujen määritelmät löytyvät myöhemmin tästä ohjeesta). (Kiinnostuneille: mediaanit ja kvartiilit kertovat palkoista enemmän kuin keskiarvo ja keskihajonta, koska palkkojen suuruudet ovat yleensä todella epätasaisesti jakautuneita, jolloin esim. pieni määrä miljardöörejä saa keskiarvon nousemaan hämäävän suureksi "normaaleihin" palkkoihin verrattuna). Koska kyseessä on Tietorakenteiden ja algoritmien harjoitustyö, ohjelman tehokkuus on tärkeä arvostelukriteeri. Vaatimuksena on vähintään keskimäärin kertaluokassa Θ(n log n) toimiva toteutus koskien kaikkia harjoitustyön operaatioita. Plussaa tietysti saa, mitä tehokkaammin operaatiot pystyy toteuttamaan. Huomaa erityisesti seuraavat: Tehokkuudessa olennaisinta on, miten ohjelman tehokkuus muuttuu datan kasvaessa, eivät pelkät sekuntimäärät. Vaadittuun tehokkuuteen ( Θ(n log n) ) lasketaan se, että ensin tyhjään tietorakenteeseen lisätään n alkiota ja sitten suoritetaan operaatio. Eli operaation tehokkuudessa ei voi "huijata" siirtämällä sitä lisäyksen yhteyteen (tietysti joissain tilanteissa lisäyksen yhteydessä saattaa silti kannattaa tehdä muitakin asioita). Plussaa tietysti saa, jos operaatioita saa toteutettua vaadittua kertaluokkaa tehokkaammin. Samoin plussaa saa, mitä nopeammaksi operaatiot saa sekunteinakin (jos siis kertaluokka on vähintään vaadittu). Mutta plussaa saa vain tehokkuudesta, joka syntyy omista algoritmivalinnoista ja suunnittelusta. (Esim. kääntäjän optimointivipujen vääntely, rinnakkaisuuden käyttö, häkkerioptimoinnilla kellojaksojen viilaaminen eivät tuo pisteitä.) Riittävän huonolla toteutuksella työ voidaan hylätä, vaikka minimitehokkuusvaatimus täyttyisikin. (Lisätty 16.2.) Esimerkkejä kysymyksistä, joilla tehokkuutta voi usein parantaa: Tehdäänkö jokin asia turhaan useaan kertaan? Voiko jonkin asian joskus jättää kokonaan tekemättä? Tehdäänkö joissain työtä enemmän kuin on välttämättä tarpeen? Voiko jonkin asian tehdä "lähes ilmaiseksi" samalla, kun tehdään jotain muuta?
Harjoitustyössä käytetyt tunnuslukujen määritelmät Tässä harjoitustyössä palkkojen vertailuissa käytetään seuraavia tunnuslukuja. Harjoitustyön yksinkertaistamiseksi niiden määritelmiä on hieman yksinkertaistettu. Alla "palkkajärjestys" tarkoittaa järjestystä pienimmäistä palkasta suurimpaan. Minimi / maksimi: Henkilö, jolla on pienin/suurin palkka. Jos tällaisia henkilöitä on useita, palautetaan jokin heistä. Mediaani: Palkkajärjestyksessä keskimmäinen henkilö. Tässä harjoitustyössä palkkajärjestyksessä henkilö indeksillä n. ( x on pyöristys alaspäin eli C++:n 2 normaali kokonaislukujakolaskun tapa pyöristää.). Ensimmäinen kvartiili: Palkkajärjestyksessä neljäsosan kohdalla oleva henkilö. Tässä harjoitustyössä palkkajärjestyksessä henkilö indeksillä n 4. Kolmas kvartiili: Palkkajärjestyksessä kolmannen neljäsosan kohdalla oleva henkilö. Tässä harjoitustyössä palkkajärjestyksessä henkilö indeksillä (3n) 4. Järjestämisestä Työntekijöitä järjestettäessä on mahdollista, että palkan mukaan järjestettäessä usealla on sama palkka (tai nimijärjestyksessä nimi). Tällaisten tapausten keskinäinen järjestys on mielivaltainen. Ohjelman hyväksymissä nimissä voi olla vain kirjaimia A-Z, a-z ja sanavälejä. Järjestämisen voi tehdä joko C++:n string-luokan vertailuoperaattorin "<" mukaan (jossa sanaväli tulee ennen kirjaimia ja isot kirjaimet tulevat ennen pieniä) tai "oikealla tavalla", jossa vastaavat isot ja pienet kirjaimet ovat samanarvoisia, mutta sanaväli tulee edelleen ennen muita kirjaimia. Rajoitukset ohjelman toteuttamisessa Harjoitustyön kielenä on C++11. Ohjelmointikielten valmiita algoritmeja (esim. std::sort, std::nth_element, std::list::sort jne.) EI saa käyttää työn tekemisessä, vaan tarvittava järjestäminen yms. toteutetaan itse. Tietorakenteena saa käyttää vain kielten tarjoamia sarjarakenteita (std::array, std::vector, std::deque, std::list), mutta EI assosiatiivisia tietorakenteita (esim. std::map, std::set). Ohjelman toiminta ja rakenne Osa ohjelmasta tulee valmiina kurssin puolesta, osa toteutetaan itse. Valmiit osat, jotka tarjotaan kurssin puolesta Tiedosto main.cpp (johon EI SAA TEHDÄ MITÄÄN MUUTOKSIA) Pääohjelma, joka hoitaa syötteiden lukemisen, komentojen tulkitsemisen ja tulostusten tulostamisen. Pääohjelmassa on myös valmiina komentoja testaamista varten.
Tiedosto datastructure.hpp struct Person: käytetään henkilön tietojen välittämiseen class Datastructure: Luokka, johon harjoitustyö kirjoitetaan. Luokasta annetaan valmiina sen julkinen rajapinta (johon EI SAA TEHDÄ MITÄÄN MUUTOKSIA) Harjoitustyönä toteutettavat osat Tiedostot datastructure.hpp ja datastructure.cpp class Datastructure: Luokan julkisen rajapinnan jäsenfunktiot tulee toteuttaa. Luokkaan saa listätä omia määrittelyitä (jäsenmuuttujat, uudet jäsenfunktiot yms.) Huom! Omassa koodissa ei ole tarpeen tehdä ohjelman varsinaiseen toimintaan liittyviä tulostuksia, koska pääohjelma hoitaa ne. Mahdolliset Debug-tulostukset kannattaa tehdä cerr-virtaan, jotta ne eivät sotke testejä. Ohjelman tuntemat komennot ja luokan julkinen rajapinta Kun ohjelma käynnistetään, se näyttää komentokehotteen ">" ja jää odottamaan komentoja, jotka on selitetty alla. Komennot, joiden yhteydessä mainitaan jäsenfunktio, kutsuvat ko. Datastructureluokan operaatioita, jotka siis opiskelijat toteuttavat. Osa komennoista on taas toteutettu kokonaan kurssin puolesta pääohjelmassa. Jos ohjelmalle antaa komentoriviltä tiedoston parametriksi, se lukee komennot ko. tiedostosta ja lopettaa sen jälkeen. Komento Julkinen jäsenfunktio add 'nimi' palkka void add_person(string name, int salary); size unsigned int size(); clear void clear(); alphalist vector<person*> personnel_alphabetically(); salarylist vector<person*> personnel_salary_order(); min Person* min_salary(); max Person* max_salary(); median Person* median_salary(); Selitys Lisää tietorakenteeseen työntekijän annetulla nimellä ja palkalla. Palauttaa tietorakenteessa olevien työntekijöiden lukumäärän. Tyhjentää tietorakenteen (tämän jälkeen size palauttaa 0). Palauttaa työntekijät nimen mukaan aakkosjärjestyksessä. Palauttaa työntekijät palkan mukaan suuruusjärjestyksessä (pienin ensin). Palauttaa työntekijän, jolla on pienin palkka (ks. ohjeista minimin määritelmä tässä työssä). Palauttaa työntekijän, jolla on suurin palkka (ks. ohjeista maksimin määritelmä tässä työssä). Palauttaa työntekijän, jolla on mediaanipalkka eli palkkajärjestyksessä keskimmäisen (ks. ohjeista mediaanin määritelmä tässä työssä).
Komento Julkinen jäsenfunktio 1stquartile Person* first_quartile_salary(); 3rdquartile Person* third_quartile_salary(); random_add n read 'tiedostonimi' stopwatch on / off / next perftest timeout n n1;n2;n3... testread 'in-tiedostonimi' 'out-tiedostonimi' help quit Selitys Palauttaa työntekijän, jolla on 1. kvartiilipalkka eli palkkajärjestyksessä neljäsosan kohdalla (ks. ohjeista kvartiilien määritelmä tässä työssä). Palauttaa työntekijän, jolla on 3. kvartiilipalkka eli palkkajärjestyksessä kolmen neljäsosan kohdalla (ks. ohjeista kvartiilien määritelmä tässä työssä). Lisää tietorakenteeseen (testausta varten) n kpl työntekijöitä, joilla on satunnainen nimi ja palkka. Huom! Arvot ovat tosiaan satunnaisia, eli saattavat olla kerrasta toiseen eri. Lukee lisää komentoja annetusta tiedostosta. (Tällä voi esim. lukea listan tiedostossa olevia työntekijöitä tietorakenteeseen, ajaa valmiita testejä yms.) Aloittaa tai lopettaa komentojen ajanmittauksen. Ohjelman alussa mittaus on pois päältä ("off"). Kun mittaus on päällä ("on"), tulostetaan jokaisen komennon jälkeen siihen kulunut aika. Vaihtoehto "next" kytkee mittauksen päälle vain seuraavan komennon ajaksi (kätevää read-komennon kanssa, kun halutaan mitata vain komentotiedoston kokonaisaika). Ajaa ohjelmalle tehokkuustestit. Tyhjentää tietorakenteen ja lisää sinne n1 kpl satunnaisia työntekijöitä. Sen jälkeen arpoo n kertaa satunnaisen komennon (random_add 1, min, max, median, 1stquartile, 3rdquartile, alphalist tai salarylist). Mittaa ja tulostaa kaikkeen tähän 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). Ajaa toiminnallisuustestin ja vertailee tulostuksia. Lukee komennot tiedostosta in-tiedostonimi ja näyttää ohjelman tulostuksen rinnakkain tiedoston outtiedostonimi sisällön kanssa. Jokainen eroava rivi merkitään kysymysmerkillä, ja lopuksi tulostetaan vielä tieto, oliko eroavia rivejä. Tulostaa listan tunnetuista komennoista. Lopettaa ohjelman. (Tiedostosta luettaessa lopettaa vain ko. tiedoston lukemisen.)
"Datatiedostot" Kätevin tapa testata ohjelmaa on luoda "datatiedostoja", jotka add-komennolla lisäävät joukon työntekijöitä ohjelmaan. Työntekijät voi sitten kätevästi lukea sisään tiedostosta read-komennolla ja sitten kokeilla muita komentoja ilman, että työntekijät täytyisi joka kerta syöttää sisään käsin. Alla on esimerkki datatiedostosta, joka löytyy nimellä example-data.txt: add 'Meikkis Matti' 2000 add 'Teikkis Terttu' 4000 add 'Miljoona Miikka' 1000000 add 'Sorrettu Sami' 1 add 'Keskiverto Keijo' 3000 add 'Kukalie Kirsi' 2500 add 'Olematon Oskari' 6000
Esimerkki ohjelman toiminnasta Alla on esimerkki ohjelman toiminnasta. Esimerkin syöte löytyy tiedostosta example-in.txt ja alla oleva tulostus tiedostosta example-out.txt. Eli voit testata esimerkin toimimista käynnistämällä ohjelman ja antamalla komennon testread 'example-in.txt' 'example-out.txt'. > clear Cleared all persons > size Number of employees: 0 > read 'example-data.txt' ** Commands from 'example-data.txt' > add 'Meikkis Matti' 2000 > add 'Teikkis Terttu' 4000 > add 'Miljoona Miikka' 1000000 > add 'Sorrettu Sami' 1 > add 'Keskiverto Keijo' 3000 > add 'Kukalie Kirsi' 2500 > add 'Olematon Oskari' 6000 > ** End of commands from 'example-data.txt' > size Number of employees: 7 > min Sorrettu Sami, salary 1 > max Miljoona Miikka, salary 1000000 > median Keskiverto Keijo, salary 3000 > alphalist Keskiverto Keijo, salary 3000 Kukalie Kirsi, salary 2500 Meikkis Matti, salary 2000 Miljoona Miikka, salary 1000000 Olematon Oskari, salary 6000 Sorrettu Sami, salary 1 Teikkis Terttu, salary 4000 > salarylist Sorrettu Sami, salary 1 Meikkis Matti, salary 2000 Kukalie Kirsi, salary 2500 Keskiverto Keijo, salary 3000 Teikkis Terttu, salary 4000 Olematon Oskari, salary 6000 Miljoona Miikka, salary 1000000 > 1stquartile Meikkis Matti, salary 2000 > 3rdquartile Olematon Oskari, salary 6000 > quit