Sivu saattaa vielä muuttua!
(Muutettu viimeksi 6.10.2010, sivu perustettu 4.10.2010.)
Nämä harjoitukset liittyvät oppimateriaalin lukuihin III, IV ja IV:1-8.
Harjoitustehtävien otsikoilla on värikoodaus: Vihreät tehtävät on syytä tehdä joka tapauksessa. Värittämättömiä ei ole ihan pakko tehdä, mutta nekin ovat hyvin hyödyllisiä ja myös vaikuttavat pisteisiin. Keltaiset tehtävät ovat vähän haastavampia. Nekin lasketaan mukaan harjoituspisteitä määrättäessä, mutta ilmankin niitä harjoituksista voi saada maksimipisteet.
Huom: Jokaisen ohjelmatiedoston alkuun on kirjoitettava kommenttina harjoituskerta, tehtävän numero ja tekijän nimi tyyliin:
// 6. harjoitukset, tehtävä 2.2, Oili Opiskelija
Tämä tehtäväsarja on kokonaisuus. Jos olet NetBeansin käyttäjä, voit tehdä sen yhteen projektiin.
Tee staattinen metodi private static int taulukonPienin(int[] taulukko)
,
joka palautaa arvonaan taulukon pienimmän alkion.
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; System.out.println("Pienin alkio on " + taulukonPienin(luvut));
Pienin alkio on -3
Tee staattinen metodi private static int taulukonPienimmanIndeksi(int[] taulukko)
,
joka palautaa arvonaan taulukon pienimmän alkion indeksin.
Entä jos pienin ei ole yksikäsitteinen?
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; System.out.println("Pienimmän indeksi on " + taulukonPieninIndeksi(luvut));
Pienimmän indeksi on 4
Tee staattinen metodi private static String taulukkoTekstiksi(int[] taulukko)
,
joka palauttaa arvonaan taulukon sisällön merkkijonona seuraavan esimerkin mukaisesti.
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; System.out.println(taulukkoTekstiksi(luvut));
8,3,7,12,-3,2,4
Tee staattinen metodi
private static void vaihdaAlkiot(int[] taulukko, int vaihdettava1, int vaihdettava2)
,
joka vaihtaa taulukon kahden alkion paikkaa annettujen indeksien vaihdettava1
ja vaihdettava2
perusteella.
Mitä metodi tekee, jos annettu indeksi on virheellinen?
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; System.out.println(taulukkoTekstiksi(luvut)); vaihdaAlkiot(luvut, 1, 0); System.out.println(taulukkoTekstiksi(luvut)); vaihdaAlkiot(luvut, 0, 3); System.out.println(taulukkoTekstiksi(luvut));
8,3,7,12,-3,2,4 3,8,7,12,-3,2,4 12,8,7,3,-3,2,4
Suositus: Virheellinen indeksi tässä tehtävässä ilman eri toimenpiteitä aiheuttaa tavallisen ArrayIndexOutOfBoundsException-virheilmoituksen ja ohjelman päättymisen. Jos haluaa hieman täsmällisemmän ilmoituksen, voi "heittää poikkeuksena" sellaisen ArrayIndexOutOfBoundsException-olion, joka sisältää tarkempaa tietoa virheen syystä. Esim. tyyliin:
throw new ArrayIndexOutOfBoundsException("Virheellinen vaihtoindeksi!");
Tee staattinen metodi
private static int pienimmanIndeksiAlkaeKohdasta(int[] taulukko, int alkaen)
,
joka palauttaa arvonaan taulukon pienimmän alkion sisältävän indeksin
ideksiväliltä alkaen
— taulukon viimeinen indeksi.
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; System.out.println(pienimmanIndeksiAlkaenKohdasta(luvut, 1)); System.out.println(pienimmanIndeksiAlkaenKohdasta(luvut, 5)); System.out.println(pienimmanIndeksiAlkaenKohdasta(luvut, 6));
4 5 6
Luennoilla tutustuttiin yksinkertaiseen vaihtojärjestämiseen. Siinä taulukon joka indeksikohtaan etsittiin "jäljellä olevista pienin". Aina kun havaittiin epäjärjestys, arvoja vaihdettiin.
Tätä algoritmia voidaan hivenen tehostaa tuossa viimeksi mainitussa kohdassa: etsitään edelleenkin "jäljellä olevista" pienin, mutta tehdäänkin tarvittaessa tuo vaihto vain "jäljellä olevista pienimmän" kanssa, ei aina, kun havaittiin epäjärjestys.
Tämän järjestämisalgoritmin nimi on vaihtojärjestäminen.
Toteuta vaihtojärjestäminen staattisena metodina
public static void jarjesta(int[] taulukko)
Käytä toteutuksessasi edellisissä tehtävissä
ohjelmoituja staattisia metodeja.
Algoritmin toimintaa voidaan kuvailla seuraavaan luettelon tapaan. [Otetaan tässä vaiheessa mukaan testitarkoituksia varten aputulostus, joka tietenkin poistetaan lopullisesta tuotteesta. Usein testitulostukset on tapana "kommentoida pois" ohjelmatekstistä! Silloin ne on myös helppo palauttaa, jos/kun algoritmin myöhemmin havaitaan toimivan väärin!]
Kokeile metodin toimintaa seuraavalla esimerkillä:
int[] luvut = {8, 3, 7, 12, -3, 2, 4}; jarjesta(luvut);
Ohjelman pitäisi tulostaa seuraavaan tapaan:
8,3,7,12,-3,2,4 -3,3,7,12,8,2,4 -3,2,7,12,8,3,4 -3,2,3,12,8,7,4 -3,2,3,4,8,7,12 -3,2,3,4,7,8,12 -3,2,3,4,7,8,12
Luennolla mittailtiin kolmen järjestysalgoritmin nopeutta eri tilanteissa. Täydennä materiaalin antamaa mittausohjelmaa VertaileJarjAlgoritmeja.java edellisen tehtävän algoritmilla ja selvitä erityisesti, onko vaihtojärjestäminen merkittävästi nopeampi kuin luennoilla esitetty yksinkertainen vaihtojärjestäminen.
Luennolla väitettiin, että binäärihaku olisi muka jotenkin kovin nopea tapa hakea järjestetystä taulukosta vastausta kysymykseen, missä jos missään indeksissä sijaitsee kysytty arvo.
Ota edellisen tehtävän mittausohjelmasta mallia ja kehittele oma mittausohjelma, joka voisi keskustella käyttäjän kanssa seuraavaan tyyliin:
Peräkkäishaun ja binäärihaun vertailua. Miten suuresta taulukosta haetaan? 100000 Montako hakua tehdään? 10000 Peräkkäishaku: 246 ms. Binäärihaku: 31 ms.
Tehdään vaiheittain kivi-paperi-sakset-peli. Pelissä on tarkoitus arvata, minkä kolmesta vaihtoehdosta (kivi, paperi tai sakset) vastustaja valitsee ja valita itse vaihtoehto, joka voittaa vastustajan valinnan. Kivi voittaa sakset, sakset voittavat paperin ja paperi kiven.
Tuomari pitää kirjaa ensimmäisen ja toisen pelaajan pisteistä sekä tasapelien määrästä.
Toteuta Tuomari-luokka, jolla on metodi
kirjaaSiirto(String ekanSiirto, String tokanSiirto)
, joka kirjaa siirron.
Siirrot ovat "k" kivelle, "p" paperille, sekä "s" saksille.
Jos yksikin parametri on virheellinen, metodi ei tee mitään.
Ohjelmoi luokkaan myös toString()
-metodi olion tilanteen merkkiesitystä
varten.
Kokeile ja esittele Tuomari-luokan käyttöä
seuraavalla Kps-ohjelmalla:
public class Kps { public static void main(String[] args) { Tuomari tuomari = new Tuomari(); System.out.println(tuomari); System.out.println(); tuomari.kirjaaSiirto("k", "p"); System.out.println(tuomari); System.out.println(); tuomari.kirjaaSiirto("k", "k"); System.out.println(tuomari); System.out.println(); } }
Ohjelman tulostus on seuraavanlainen:
Pelitilanne 0-0 Tasapelejä 0 Pelitilanne 0-1 Tasapelejä 0 Pelitilanne 0-1 Tasapelejä 1
Laajenna pääohjelmaa Kps siten, että se kysyy syötteitä kahdelta pelaajalta. Peli jatkuu niin pitkään kunnes jompikumpi pelaajista antaa syötteeksi jonkin muun syötteen kuin "k", "p" tai "s". Käytä Tuomari-luokkaa pelitilanteen tulostamiseen jokaisen kierroksen jälkeen.
Keskustelun pitää näyttää seuraavanlaiselta:
Ensimmäisen pelaajan siirto: p Toisen pelaajan siirto: p Pelitilanne 0-0 Tasapelejä 1 Ensimmäisen pelaajan siirto: s Toisen pelaajan siirto: p Pelitilanne 1-0 Tasapelejä 1 Ensimmäisen pelaajan siirto: s Toisen pelaajan siirto: k Pelitilanne 1-1 Tasapelejä 1 Ensimmäisen pelaajan siirto: s Toisen pelaajan siirto: riitti! Kiitos! Pelitilanne 1-1 Tasapelejä 1
Tee luokka Tekoaly, jolla on metodi getSiirto()
. Voit itse päättää,
millä perusteella siirto annetaan. Tekoaly-olio ei kuitenkaan saa tietää ennalta
vastapelaajan siirtoa.
Laajenna luokkaa Kps
siten, että peli kysyy aluksi pelataanko
tietokonetta vastaan. Jos pelaaja vastaa "k" tai "kyllä", peli on tietokonetta
vastaan. Muuten pelataan toista pelaajaa vastaan.
Pääohjelman tulostus on seuraavanlainen jos valitaan pelaaja pelaajaa vastaan.
Pelataanko tietokonetta vastaan: ei! Ensimmäisen pelaajan siirto: p Toisen pelaajan siirto: p Pelitilanne 0-0 Tasapelejä 1 Ensimmäisen pelaajan siirto: s Toisen pelaajan siirto: riitti! Kiitos! Pelitilanne 0-0 Tasapelejä 1
Jos valitaan vastustajaksi tietokone, tietokoneen valinnat tulostetaan myös.
Pelataanko tietokonetta vastaan: k Ensimmäisen pelaajan siirto: p Tietokone valitsi: k Pelitilanne 1-0 Tasapelejä 0 Ensimmäisen pelaajan siirto: k Tietokone valitsi: p Pelitilanne 1-1 Tasapelejä 0 Ensimmäisen pelaajan siirto: riitti! Kiitos! Pelitilanne 1-1 Tasapelejä 0
Laajenna Tekoaly-luokkaa siten, että se pitää kirjaa vastustajan
tekemistä siirroista. Metodi asetaSiirto(String siirto)
asettaa
vastustajan siirron tekoälyn muistiin. Toteuta tekoälyn muisti String-taulukkona,
jonka koko voidaan määritellä Tekoaly-luokan konstruktorissa.
Muistin täyttyessä tekoäly unohtaa aina vanhimman siirron.
Muuta myös Tekoaly-luokan getSiirto
-metodia siten, että se
käyttää muistissa olevaa taulukkoa seuraavan siirron tekemiseen.
Kannattaa ehkä verrata vastustajan viimeistä siirtoa aikaisempiin siirtoihin ja
pohtia minkä verran mitäkin siirtoa on tehty aikaisemman siirron jälkeen.
Esimerkiksi, jos vastustajan viimeisin nähty siirto on k
,
ja muistissa on ppkspkkspk
, on vastustaja pelannut kiven jälkeen kerran
kiven ja kaksi kertaa sakset. Tällöin tekoälyn ehkä kannattaa pelata kivi.
Saat toki kehitellä monimutkaisemmankin tekoälyn!
Monet tehtävät edellä on tehty ohjatusti vaiheittain. Nyt on aika kokeilla omia siipiä! Pyri tekemään myös seuraavat tehtävät askelittain etenemällä.
Tee sovellus, joka tulostaa komentoriviparametrit String-vertailujen antamassa "aakkosjärjestyksessä".
Tee ohjelma, joka ensin kysyy syötettävien kokonaislukujen lukumäärän, sitten lukee tuon määrän lukuja taulukkoon ja lopuksi tarjoaa seuraavan palvelun: Ohjelmalle annetaan lukuja yksi kerrallaan ja ohjelma selvittää, löytyykö luku taulukosta. Etsimiseen on käytettävä binäärihakua. Muista mitä binäärihaku edellyttää taulukon järjestykseltä!
Lotto on numeroveikkaus, jossa arvotaan 7 numeroa ja 3 lisänumeroa 39 numerosta. Loton voittoluokat ovat 7 oikein, 6 ja lisänumero oikein, 6 oikein, 5 oikein ja 4 oikein. Tee ohjelma joka ensin arpoo oikean lottorivin. Sitten ohjelmalta voi kysellä, onko jokin lottorivi oikein. Kyselyitä voi olla useampia. Suunnittele itse, miten ohjelman toiminta päättyy. Satunnaisluvun väliltä 1-39 saat arvottua vaikkapa seuraavasti:
int arvottu = (int)(39*Math.random()) + 1;
Kyselija on olio, "laite", joka helpottaa tietokilpailun kysymysten tekemistä ja vastausten analysointia. Sen API on seuraavanlainen:
kysymyksenAlku kysytty sana kysymyksenLoppu
OIKEIN!
tai:
VÄÄRIN! Oikea vastaus on "oikeaVastaus".
Ks. myös esimerkki alla.
Käyttöesimerkki selventää konstruktorin ja aksessoreiden käyttöä:
Kyselija kilpa = new Kyselija("Mitä on italian", "suomeksi?"); kilpa.samaistaIsotJaPienet(true); boolean oikein; oikein = kilpa.kysy("tutti", "kaikki"); if (oikein) System.out.println("Hienoa, hyvä alku!"); // kuvaruudulla tapahtuu: // // Kone: Mitä on italian "tutti" suomeksi? // Käyttäjä: Kaikki // Kone: OIKEIN! // Kone: Hienoa, hyvä alku! kilpa.kysy("ferro", "rauta"); // Arvon palauttavaa metodia voi // kutsua myös arvoa käyttämättä! // kuvaruudulla tapahtuu: // // Kone: Mitä on italian "ferro" suomeksi? // Käyttäjä: rAuTA // Kone: OIKEIN! oikein = kilpa.kysy("matto", "hullu"); if (!oikein) System.out.println("No mitäs nyt?"); // kuvaruudulla tapahtuu: // // Kone: Mitä on italian "matto" suomeksi? // Käyttäjä: matto // Kone: VÄÄRIN! Oikea vastaus on "hullu". // Kone: No mitäs nyt? System.out.println("Kysymyksiä oli " + kilpa.montakoKysymystä() + ", oikeita vastauksia " + kilpa.montakoOikein() + "."); // kuvaruudulla tapahtuu: // // Kone: Kysymyksiä oli 3, oikeita vastauksia 2. kilpa.kysy("madre", "äiti"); kilpa.kysy("cavallo", "hevonen"); kilpa.kysy("birra", "olut"); // ... jne., jne...
Ohjelmoi myös sovellus, joka havainnollistaa Kyselija-luokan käyttöä tietokilpailun toteuttamisessa. Voit tietenkin valita itseäsi kiinnostavan aihepiirin. Huomaa että tämä tehtävä ei ole taulukkotehtävä vaikka tässä osassa onkin.
Vastaa kurssikyselyyn osoitteessa http://ilmo.cs.helsinki.fi/kurssit/servlet/Valinta. Valitse Ohjelmoinnin perusteet.
Muista myös lähettää lomake! Lähetysnäppäin on lomakkeen lopussa. Vastaukset ovat anonyymejä, joten ei tarvitse ujostella... Ja tästäkin tehtävästä silti saa "rastin", kunhan vain vakuuttaa ohjaajan siitä, että vastattu on.
Kyselylomakkeessa on joukko kohtia joihin ei voi syystä tai toisesta vastata. Jätä sellaiset kohdat väliin. Erityisesti koetta koskevat kysymykset ovat tällaisia. Samoin kysymys 15 "harjoitusryhmän pitäjästä" ei ole tässä kurssityylissä helppo. Vastaa siihen vain, jos olet selkeästi työskennellyt pääasiassa yhden ohjaajan kanssa.
Vastauksilla ihan oikeasti on merkitystä laitoksen opetusta kehitettäessä! Erityisen kiinnostavaksi ja tärkeäksi vastaamisen tekee nyt kokeiltu uudenlainen tapa järjestää kurssi.