Demo 10
Palauta viimeistään ma klo 11:59.
Ajankäyttösi tällä viikolla
Työtuntien kirjaamisesta saa 0.5 demopistettä. Aseta pisteesi Set Custom Points -toiminnolla.
Demokerran päätteeksi kirjoita arvio tällä viikolla käyttämästäsi työmäärästä. Laske työmäärään mukaan kaikki tällä viikolla käyttämäsi aika: lähiopetukseen osallistuminen, oppimateriaalin lukeminen, demotehtävien tekeminen, luentovideon katsominen, demojen purkutilaisuuteen osallistuminen (maanantaina) ja niin edelleen. Voit päivittää lukua viikon edetessä, se voi helpottaa arvion tekemistä. Huom! Siis tähän ei laiteta kurssin alusta kaikkia tunteja yhteensä, vaan vain tämän viikon tunnit.
Jokaisella opiskelijalla on oikeus ja velvollisuus arvioida itse omien pisteidensä oikeellisuus. Mikäli ohjelmasi ei toimi, niin aseta itsellesi Custom pointsit itsearviosi mukaisesti, esim. mikäli ajattelet että olet saavuttanut 50% tehtävän tavoitteista niin silloin laitat itsellesi enintään 0.5 pistettä. Vastaavasti, jos arvioit että tehtäväsi on 90%:sti oikein, mutta automaattinen arviointi antaa sinulle jostain syystä vähemmän pisteitä, aseta itsellesi 0.9 pistettä.
PP5, perjantai 19.3.
PP5-tehtävät. Näistä saa pisteitä vain käymällä PP-ryhmässä Teamsin PP-kanavalla perjantaisin klo 14:15-16:00.
Oppimistavoitteet
Tämän demokerran jälkeen
- muistat mitä rekursio tarkoittaa ja miten tehdään rekursiivinen funktio
- osaat soveltaa silmukoita kombinaatiotehtävään
- olet tutustunut poikkeuksiin (Exception) ja poikkeusten käsittelyyn
Tehtävä 1-2* (1-2 p.)
M: 22. Rekursio.
Tee alla olevaa mallikoodia muokaten ohjelma, joka piirtää kuvan kaltaisen puun rekursiivisesti. Halutessasi voit katsoaa
ideaa Sierpinskin kolmion piirtämisestä.
Anna itsellesi enintään 2 pistettä Set custom points
-toiminnolla.
HUOM : Jos tehtävä ei aukea, älä aloita tällä tehtävällä, vaikka se periaatteessa onkin helppo.
Beginissä: Hyvä alkukulma piirtämiseen on vaikkapa pii/2 (saat piin arvon kirjoittamalla Math.PI
). Pallon säteeksi voit antaa vaikka 100.
Pallo-aliohjelmassa: Kokeile vaihdella suuntaa ja sädettä muuttavia vakioita ja katso miten ne vaikuttavat kuvaan.
Tehtävä 3
M: 23. Dynaamiset tietorakenteet. Tee lotto-ohjelma, joka arpoo 7 numeroa ja 3 varanumeroa 40:stä.
Sotkemisen voit tehdä myös lisäämällä projektin referensseihin Jypeli:n ja sitten
Jypeli.RandomGen.Shuffle(pallot);
Tai voit ottaa alta valmiin sekoitusalgoritmin.
/// <summary>
/// Aliohjelma sekoittaa annetun kokonaislukutaulukon
/// alkiot keskenään Fisher-Yates-algoritmilla
/// (aikavaativuus n).
/// http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
/// </summary>
/// <param name="luvut">Sekoitettava taulukko.</param>
public static void SekoitaTaulukko(int[] luvut)
{
Random r = new Random();
for (int i = luvut.Length - 1; i > 0; i--)
{
int j = r.Next(i);
int apu = luvut[j];
luvut[j] = luvut[i];
luvut[i] = apu;
}
}
Tehtävä 4-5 (1-2 p.)
Tee funktio GeneroiSalasana
. Funktio ottaa parametreina seuraavat tiedot:
- salasanan pituus (int),
- voiko salasana sisältää suuraakkosia (bool),
- voiko salasana sisältää numeroita (bool),
- voiko salasana sisältää erikoismerkkejä (bool)
Funktio palauttaa salasanan näiden parametrien perusteella.
Erikoismerkkejä ovat:
!@#$%&-
Muita erikoismerkkejä ei tässä oteta huomioon.
Esimerkiksi kutsu GeneroiSalasana(r, 10, true, true, true)
voisi palauttaa rV30b5E!o1
.
Vinkkejä:
- Tarvitset ohjelmassasi satunnaisuutta käyttäen Random-oliota. Esimerkiksi luvun arpominen väliltä 0-9 onnistuu seuraavasti.
- Kirjaimen arpominen: (1) Luo sopivista pienaakkosista yksi pitkä merkkijono, (2) arvo
Random
-olionNext
-metodin avulla kokonaisluku väliltä [0, ... merkkijonon pituus], ja (3) ota merkkijonosta kirjain arpomasi luvun mukaisesta indeksistä. - Erikoismerkin arpominen: Samoin näistä kannattaa tehdä yksi merkkijono ja arpoa siitä yksi.
- "Kolikon heiton" (esim. tuleeko yksittäisen kirjaimen olla suuraakkonen vai ei) saat toteutettua esimerkiksi käyttämällä Random-olion NextDoublea, joka antaa desimaaliluvun väliltä 0-1:
Huomautus 1: Useiden Random-olioiden luomista pitäisi C#:n dokumentaation mukaan välttää. Niinpä jos haluat generoida salasanoja silmukassa (esimerkiksi pääohjelmassa), tulee Random-olio r
luoda yhden kerran koko ohjelmassa (pääohjelman aluksi) ja antaa sitten r
argumenttina, sillä muuten arpominen ei käytännössä onnistu. Luonnollisesti Random-olio pitää tällöin lisätä funktion esittelyriville parametrilistaan ensimmäiseksi. Esimerkki pääohjelmasta (voit copy-pastettaa tämän itsellesi):
Random r = new Random();
for (int i = 0; i < 10; i++)
{
Console.WriteLine(GeneroiSalasana(r, 10, true, true, true));
}
Huomautus 2: Olisi hyvä arvioida, kuinka todennäköisiä kunkin merkin esiintyminen on. Esimerkiksi ovatko kaikki merkit yhtä todennäköisiä, vai onko tietty merkki yhtä todennäköisesti (a) kirjain, (b) numero tai (c) erikoismerkki? Tähän ei ole yhtä ja oikeaa vastausta.
Arviointi: Käytä Set Custom Pointsia. Jos salasanan tuottaminen toimii "jotenkin" voit merkata itsellesi yhden pisteen. Jos kaikkien parametrien huomioon ottaminen toimii, voit merkata kaksi pistettä.
Mistä löytyisi vinkkejä/esimerkkejä siitä, että merkkijonosta arvotaan yksi(?) kirjain? Pitääkö tätä tehtävää varten ottaa käyttöön using Jypeli -referenssi?
Ei tarvita Jypeli-referenssiä. Muokkasin vinkkiä. Olisiko se nyt selkeämpi. -AJL
Kiitos! On selkeämpi näin.
—Heittää varoitusta / virhettä: "generoisalasana.cs(7,15): warning CS8321: The local function 'GeneroiSalasana' is declared but never used". Ei mahdu meikän lanttuun syy. Toimii Riderissa kuitenkin erinomaisen hienosti. Lisäksi "modifier 'public' is not valid for this item". Kokeilin myös testiksi kirjoittaa Main-aliohjelman, mutta kaksi ohjelman aloituskohtaa ei kuulemma kelvannut...
Kirjoita luokka ja Main mukaan myös. C#:ssa voi tosiaan (nykyään) kirjoittaa myös "luokattomia" ohjelmia, ja sellaiseksi sinun ratkaisusi kääntäjä tulkitsee. Niinpä jos (vaihtoehtoisesti) laitat funktion loppusulun jälkeen rivin Console.WriteLine(GeneroiSalasana(10, true, true, true));
niin tulostus pitäisi näkyä. -AJL
Tehtävä 6
M: 24. Poikkeukset. Kirjoita funktio MuutaJono(String s, double oletus)
, siten, että seuraava ohjelma toimii:
Huom Jos kotikoneesi asetukset ovat suomalaisittain, Double.Parse
-metodi olettaa argumentin erotinmerkiksi pilkun. Timissä erotinmerkkinä on piste. Jos haluat omalle koneellesi pisteen erotinmerkiksi, laita Parse
-metodin argumentiksi:
new System.Globalization.CultureInfo("en-US")
Tämä funktio yrittää siis poimia annetusta merkkijonosta liukuluvun (double
) ja palauttaa sen. Mikäli tämä ei onnistu, palauttaa se oletusarvon. Funktiolle on oikeassa elämässä käyttöä koska käyttäjältä saatu syöte on aina merkkijono ja se pitää pystyä muuttamaan reaaliluvuksi laskemista varten. Tuo desimaalierotin on vaikeampi. Parse
käyttää sitä erotinta, mikä on järjestelmään asetettu. Oikeasti pitäisi siis vielä osata tulkita oikealla tavalla riippumatta siitä, antaako käyttäjä pilkun vai pisteen. Tässä tehtävässä tuosta ongelmasta ei tarvitse välittää vaan riittää yllä olevaan esimerkkiin käsin vaihtaa piste pilkun tilalle jos järjestelmässäsi on piste desimaalierottimena (C#-koodin vakioissahan se on aina piste).
Kun toteutat funktiota MuutaJono
, käytä hyväksi Double
-luokan funktiota Double.Parse ja sitä, että jos muuttaminen ei onnistu, Parse
heittää poikkeuksen.
V1
Tee Ville-tehtävät: 8.1-8.5. Muista: Villen käyttöohje ja Ville-tehtävien palauttamisohjeet.
TDD1
Jos tarkistat vähintään kahden funktion toiminnan automaattisella testillä (ComTest), saat merkitä yhden lisäpisteen. Voit antaa samassa tiedostossa palautetta ja kehitysehdotuksia Comtestin käytöstä.
Mikäli ComTest ei toimi yliopiston mikroluokissa, kokeile ensin asentaa ComTest-VS-plugin. Sitten tarkista, että ComTest haetaan oikeasta paikasta: Valitse Visual Studiossa Tools -> ComTest -> Options. Tarkista, että Path to ComTest.jar
executable kentässä on N:\bin\ComTest.jar
ja olet yhdistänyt koneesi N-verkkolevyyn. Kysy tarvittaessa ohjaajalta neuvoa.
B1-2
M: 25. Tietojen lukeminen ulkoisesta lähteestä.
Avaa sivu ja klikkaa sivun "Csv-tiedosto". Huomaa että sivu antaa kuitenkin .xls tiedoston nappulan tekstistä huolimatta. Tästä ei tarvitse erityisesti välittää. Muuta tiedoston nimeksi valuutat.xls. Lisää projektiisi tämä tiedosto klikkaamalla projektin nimen päällä hiiren kakkosnäppäimellä Add -> Existing Item). Laita tiedosto myös kopioitumaan bin-hakemistoon klikkaamalla tiedoston päällä hiiren oikealla -> Properties -> Copy to output directory -> Always copy.
Ohjelma toimii Riderissa, mutta en saanut Timissä lähellekään toimimista. Custom pointseja ei pysty laittamaan.
Custom pointsit toimii nyt. Korjaan koodin huomenna. -AJL
Nyt kun klikkaat Alusta niin voi muokata valmista pohjakoodia. -AJL
—Tiedosto näyttää kutakuinkin tältä
EUR,000001.000000,000001.000000,000001.000000,23/03/2023,08:00
USD,000001.090550,000001.067100,000001.114000,23/03/2023,08:00
JPY,000142.624000,000139.552000,000145.696000,23/03/2023,08:00
Tiedosto voidaan nyt lukea seuraavasti. Lisää ensin Using-lauseisiin lause
using System.IO;
Sitten Main
-pääohjelmaan (tämä on annettu palautuslaatikon pohjakoodissa)
string[] valuuttalista;
try
{
valuuttalista = File.ReadAllLines("valuutat.xls");
}
catch (IOException ex)
{
Console.WriteLine("Virhe: " + ex.Message);
return;
}
Tee aliohjelma KyseleValuuttoja
, joka kysyy silmukassa käyttäjältä valuuttatunnuksen, ja sitten tulostaa annetun valuutan tiedot ruudulle hieman mukavammin muotoiltuna. Luvut parsitaan double.Parsella jolloin ylimääräisiä nollia ei näytetä.
Anna valuutta >USD
USD: Keskikurssi 1.09055. Myynti 1.0671. Osto 1.114. (23/03/2023)
Anna valuutta >JPY
JPY: Keskikurssi 142.624. Myynti 139.552. Osto 145.696. (23/03/2023)
Ohjelma päättyy, kun käyttäjä antaa tyhjän syötteen.
Huomaa että TIMissä pitää varautua myös siihen, että lukemisesta voi tulla null
:
string komento = Console.ReadLine();
if ( komento == null || komento.Equals("") ) break;
Kirjoita tähän tekemäsi tiedosto ja tallenna ennen kuin yrität käyttää sitä ohjelmassa.
Onkohan tässä vielä vähän bugia, kun TIM vaikuttaisi ottavan käyttäjän syötteen tuosta ylimmästä laatikosta, johon pitäisi ilmeisesti kirjoittaa valuutat.xls-tiedoston sisältöä? Vai olenko ymmärtänyt väärin?
Korjattu. -AJL
—B3-4
M: 16. Toistorakenteet. Erilaisten 7 numeroa sisältävien lottorivien määrä saadaan binomikertoimen kaavasta: Katso: http://fi.wikipedia.org/wiki/Kombinaatio.
\[\binom {40}{7} = \frac {40!}{7! * (40-7)!} = \frac{34*35*36*37*38*39*40}{ 1*2*3*4*5*6*7} = 18643560\]
Tulos voidaan laskea käyttämällä long -tyyppisiä lukuja. Tee funktio
long NYliK(int n, int k)
jonka kutsulla
NyliK(40, 7)
saat mainitun tuloksen. Tulosta ei voi laskea keskimmäisestä kaavasta 2, koska \(40!\) ylittäisi reilusti pitkienkin (long) kokonaislukujen lukualueen.
Vinkki: Tässä tehtävässä ei tarvita listoja tms., pelkästään kerto- ja jakolaskuja sekä silmukoita. Vastaus saattaa olla nätimpi, jos avuksi kirjoittaa yhden pienen aliohjelman.
B5
Tutki miten C#:issa toimii BigInteger -luokka ja tee NYliK(int n,int k)
sen avulla. Tee siis tehtävää 4 vastaava toteutus erityyppisellä kokonaisluvulla. BigInteger luokan käyttö vaatii Numerics-assemblyn käyttöä. Lisää ohjelman alkuun
using System.Numerics;
Täysiin pisteisiin vaaditaan omat testit: Tehtävässä saa oikeasta tulosteesta 0.5 p., testien läpäisemisestä 0.4 p. ja dokumentaation katsomisesta 0.1 p.
G1-2
Tutki miten C#:ssa voidaan lukea syötettä WWW-osoitteesta ja tee tehtävä B1-2 niin, että tiedot luetaan suoraan netistä.
Voit lukea tiedostoa osoitteesta
https://tim.jyu.fi/files/183746/commercialTransaction.xls
Tämän sai toimimaan TIMissä (ja pisteet), kun otti valuuttatietojen kysymisen pois silmukasta (eli toisin kuin se käsketään toteuttamaan tehtävässä B1-2)
—G3-4
PNS: Pienimmän neliösumman sovitus on eräs tapa laskea eräänlainen "keskiluku" tai trendi aineistolle. Esimerkiksi meillä on havaintopisteitä, joiden periaatteessa pitäisi muodostaa "suora". Laskemalla PNS-suoran (y = a + bx) kertoimet a ja b voimme piirtää aineistoa parhaiten kuvaavan suoran. Katso http://mathworld.wolfram.com/LeastSquaresFitting.html kertoimien laskukaavat (12) ja (14) ja tee ohjelma, joka piirtää aineiston ja sitä kuvaavan PNS-suoran. Aineisto piirretään tämän pohjatiedoston: Kuvaaja.cs avulla, eli voit käyttää siellä olevaa koodia pohjana itse aineiston piirtämiseen. Suora piirretään Paint
-metodissa kutsumalla canvas.DrawLine
-metodia. Esimerkki: pns.png (tuossa Javalla tehdyssä kuvassa pallot eivät ole täytettyjä, sitä ei tarvitse yrittää).
G5-6
Euler problem 67. Laskun pitää mennä alle minuuttiin. Yksi lisäpiste jos lasku menee "erittäin nopeasti" (luokkaa millisekunteja.)
G10-15 (5 p.)
Steganografia: Tee funktio (ja tarvittavat apufunktiot) joilla saat salakirjoitettua merkkijonon Jypelin Image-kuvaan siten, että viesti koodataan kunkin värikomponentin (r, g, b, a) kahteen vähiten merkitsevään bittiin. Tee myös funktio jolla saat luettua viestin tällaisesta kuvasta.
Voit olettaa, että viesti tulee 8-bittisinä merkkeinä. Char-merkin 8 vähiten merkitsevää bittiä saat otettua sanomalla int lowerBits = myChar & 0xff;
Tämä riittää ihan hyvin esim. englannin ja suomen kieleen. Toki erikoisemmat merkit jäävät tällä tavalla pois.
Pisteytys: Set Custom Points. 3 p. kun saat salakirjoitettua viestin kuvaan, 2 p. lisää kun saat luettua viestin kuvasta.
Olkoon meillä kuvapikseli, jonka värikomponentit ovat
desimaalilukuina:
r=63, g=51, b=25, a=255
binäärilukuina:
r=00111111, g=00110011, b=00011001, a=11111111.
Sovitaan, että meidän koodattava merkki on M (desimaalilukuna 77, bittiesitys 01001101) niin salakirjoituksen tuloksena kuvapikselin uudet väriarvot ovat:
r=00111101 (M-kirjaimen kaksi eniten merkitsevää bittiä on 01, jotka
asetetaan tämän värikomponentin kahdeksi oikeanpuolimmaiseksi bitiksi)
g=00110000 (M-kirjaimen kaksi seuraavaa bittiä on 00)
b=00011011 (jatketaan vastaavasti, bitit 11)
a=11111101 (jatketaan vastaavasti, bitit 01)
Image-kuvan saat byte-taulukoksi (luvut väliltä 0..255) sanomalla kuva.GetByteArray()
.
Extension-metodi Image-luokkaan, joka asettaa byte-taulukon arvot suoraan kuvapikseleiksi. Tämä löytyy Jypelin versiosta 8.1.0 alkaen, mutta mikäli sinulla on käytössä vanhempi versio, lisää alla oleva projektiisi mukaan. Kutsu tätä sanomalla kuva.SetData(munByteArray, korkeus, leveys);
namespace Jypeli
{
public static class ImageExtension
{
public static void SetData(this Image image, byte[] salakirjoitettu, int kuvanKorkeus, int kuvanLeveys)
{
Color[,] salakirjoitettuColor = new Color[kuvanKorkeus, kuvanLeveys];
for (int i = 0; i < kuvanKorkeus; i++)
{
for (int j = 0; j < kuvanLeveys; j++)
{
int r = salakirjoitettu[4 * (i * kuvanLeveys + j) + 0];
int g = salakirjoitettu[4 * (i * kuvanLeveys + j) + 1];
int b = salakirjoitettu[4 * (i * kuvanLeveys + j) + 2];
int a = salakirjoitettu[4 * (i * kuvanLeveys + j) + 3];
salakirjoitettuColor[i, j] = new Color(r, g, b, a);
}
}
image.SetData(salakirjoitettuColor);
}
}
}
Salakirjoitettavat merkit pitää pätkiä siis kahden bitin jonoihin. Muuta ensin merkki int
-luvuksi. Sen jälkeen tämän merkin kaksi vähiten merkitsevää bittiä saat käyttämällä bittimaskia 0x3
: int bits01 = myNumber & 0x3;
Tässä 0x3
on siis luku 00000011
heksadesimaalilukuna ja &
-operaattorin ansiosta bits01
-muuttujaan tallennetaan tasan ne luvut jotka sattuvat myNumberissa
olemaan kahdessa ekassa bitissä.
Kun nämä bitit on nyt "käytetty", niin voit rullata bittejä oikealle päin sanomalla myNumber = myNumber >> 2;
. Sitten seuraavat bitit voit ottaa käyttämällä yllä esiteltyä tapaa (laita muuttujalle nimeksi vaikka bits23
.
Sitten kirjoittaessasi tätä salakirjoitettua dataa alkuperäisen kuvan päälle käytä &
(bitwise and) ja |
(bitwise or) operaattoreita sopivasti.
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.