Luin Dimensiosta Hannu Korhosen kirja-arvion Tapio Lindin Vesimelonin kuivatus ja muita matemaattisia pähkinöitä -kirjasta. En ole vielä ehtinyt lukemaan kirjaa, mutta se pitänee lukea lähiaikoina. Kirjassa yhtenä ongelmana on kuuluisa Monty Hall-probleema. Aihe on kuikertanut mielessäni jo jonkin aikaa. Niinpä päätin tehdä siitä pienen Python ohjelman. Lähinnä itselleni ja samalla myös oppilaitani varten esimerkkinä siitä, miten sanallinen ongelma käännetään tietokoneohjelmaksi.
Ongelma sanallisesti
Wikipediassa Monty Hall -ongelma esitetään seuraavasti: ” Monty Hallin ongelmassa kilpailijalla on edessään kolme ovea. Yhden oven takana on palkintona auto, kahden muun takana vuohi. Kilpailija, joka ei tiedä minkä oven takana mikin palkinto on, saa valita ovista yhden. Valittuaan oven hän ei vielä avaa sitä. Jäljelle jääneistä kahdesta ovesta avataan toinen, ja sen takana on aina vuohi. Tämän jälkeen kilpailija saa valita, vaihtaako ensin valitsemansa oven toiseen jäljellä olevaan suljettuun oveen, vai pitääkö ensin valitsemansa oven.”
Varmaankin kaikki matikan opet tietävät (viimeistään lukemalla ratkaisun Wikipediasta), että kilpailijan kannattaa vaihtaa, sillä pitämällä ensimmäisen valinnan, niin todennäköisyys voittoon on 1/3 ja vaihtamalla 2/3.
Python koodi
Tehdään Pythonilla ohjelma, joka noudattaa edellä esitettyä tilannetta siten, että kilpailija vaihtaa aina valintansa. Koodia kirjoittaessa en käytä järkeä siinä mielessä, että oikaisisin suoraan ratkaisuun. Toteutan tilanteen ikään kuin ohjeen mukaan. Laitan koodiin rivinumerot ja kommentit, joiden avulla yritän kertoa lukijalle mitä koodissa tapahtuu.
Muutama kommentti koodista
Lukijan on hyvä muistaa, että Pythonissa listan ensimmäisen jäsenen järjestysluku on nolla, toisen järjestysluku on 1 jne. Siksi ovien numerot ovat 0, 1, ja 2.
Rivillä 9: shuffle(lista) funktio sekoittaa syötelistansa jäsenet satunnaisesti.
Rivillä 13: randint(a, b) tuottaa satunnaisen kokonaisluvun väliltä [a, b].
Rivillä 17: pop-metodi poistaa olion listasta ja antaa sen tulosteena. Jos lista = [13, 42, 666, 42], niin lista.pop(1) antaa tulokseksi luvun 42 ja samalla listasta katoaa jäsen 42, eli nyt lista on [13, 666, 42].
Rivillä 20: remove-metodi poistaa ensimmäisen esiintymän syötteestään. Jos lista = [13, 42, 666, 42], niin lista.remove(42) muutaa listan listaksi [13, 666, 42].
Rivillä 23: Leikkauksien avulla saadaan valittua listasta jäseniä. Jos lista on lista = [13, 42, 666, 42], niin lista[1] tulostaa luvun 42.
Alla ohjelman tuottama tulos muutamalla suorituskerralla.
Kun tuota ohjelmaa ja tulosteita katsoo, niin pikku hiljaa (ainakin minulle) selkenee, että tässä pelissä pelaaja voittaa, jos hän on alun perin valinnut vuohen. Vuohi valitaan todennäköisyydellä 2/3.
Tehdään vielä ohjelmaan sellainen muutos, että suoritetaan se vaikkapa 1000 kertaa ja lasketaan voittojen lukumäärä.
Alla ohjelman tulos:
Seuraavassa tarinassani muokkaan ongelmaa/ohjelmaa sellaiseksi, että vuohia onkin vaikkapa 5 ja voittoautoja 2. Ja samalla tietysti pohdin, mikä siinä tilanteessa on voiton todennäköisyys.
Itsenäisyysjuhlaviikonlopun perjantaiaamuna seitsemän pintaan Whatsuppiini kilahti viesti Hyllin musiikinopettajalta Eriikalta – koulussa on pieni hätä. Soitin. Hyllin ylioppilasjuhliin oli tilattu ammattilainen kuvaamaan ja julkaisemaan Hyllin itsenäisyysjuhla. Molemmat tekijät olivat sairastuneet koronaan.
alku
Vielä unihiekkaa silmissä kerroin, että onhan siellä henkilöitä koulussa, joilla olisi nyt näytön paikka. Pohdittiin myös mahdollisia naapurin lukiolaisnaapureita ja serkunpojanäitejä, jotka voisivat hoitaa asian. Ajattelin itse pitää oppituntini normaalisti kotoa koronaeristyksestä.
Kun olin juonut aamukahvini ja hieman ajatellut asiaa, niin päätin, että jos kerran Eriikka kantaa varsinaisen, niin otanhan minä jalustan. Toisaalta ylioppilaissa oli monta oman ryhmäni oppilasta ja esiintyjät olivat harjoitelleet juhlaa varten. Noiden oppilaiden takiahan tätä työtä tehdään. Soitin rehtori Jussille ja kerroin että lähden ajelemaan koululle. Ennen lähtöä ehdin laittaa Classroomviestin ensimmäisen tunnin oppilaille, että laskekaa jotain. Lähtiessä tarkistin, että kaikki on mukana, mutta silti unohdin oman kamerani jalustan. Matkalla muistin sen. Tiesin, että kuviksessa on parikin jalustaa, mutta aina ei voi olla varma, että löytyykö ne helposti. Päätin myös, että vaikka kuinka paljon tilanne kiukuttaisi, niin en kiukuttele oppilaille ja yritän pitää positiivista henkeä yllä itselläni ja koko tiimillä.
Olin koululla noin puoli kahdeksan. Jarno soitteli ja tarjosi apua, kerroin pyytäväni, kun tarvitsen. Kun pääsin koululle, niin pyysin Samia hakemaan kuviksesta jalustan. Juhlasalissa näin, että joku, luultavasti Jorma oli käynyt hakemassa kukkia koristeluun, Tarja oli koristelemassa puhujapönttöä ja lakkiaispöytää. Eriikka ja Jussi olivat jo valmiina juhlasalissa. Aloitin Meetin ekan tunnin oppilaille, kerroin tilanteesta. Samalla laitoin viestiä toisen tunnin oppilaille, että en ole paikalla opettamassa.
kuvaus
Sami auttoi näyttämön valaistuksen kanssa ja Jorma sammutti ilmastoinnin. Varsinaiseen kuvaukseen päästiin noin 8.50. Parin testivideon jälkeen saatiin Jussin tervetulotoivotus ja puhe purkkiin. Tässä vaiheessa huomasin, että kameran akku on lopussa. Onneksi minulla on kamerakassissa toinen akku ja laturi, niinpä pystyin kuvauksien aikana latailemaan akkuja sitä mukaa kun ne tyhjenivät. Jussin puhe onnistui ensimmäisellä otolla. Tässä vaiheessa kello oli 9.30.
Koska en harrasta videokuvausta, niin hankaluuksia tulee mm. siitä, että minulla ei ole kokemuksia aukon säätämisestä videon yhteydessä yms. eli on pakko käyttää automaattiasetuksia. Toisaalta en voi käyttää automaattitarkennusta, koska se aiheuttaa videoon ääniä, jos kamera tarkentaa. Olen tottunut tarkentamaan normiokulaarista, niinpä kuvan tarkentaminen pikkunäytöltä on haastavaa. Vanhaa Nikon 5100-kameraa ei ole varsinaisesti suunniteltu videokuvaukseen. Myöskään keväällä hankkimani mikrofoni ei ole maailman parhaasta päästä.
Sitten oli vuorossa ylioppilaiden lakitus ja Gaudeamus Igidur tämänkin saimme nauhoitettua ensimmäisellä otoksella. Kello oli 10.52.
Seuraavaksi kuvattiin ylioppilaiden puhe, sekin onnistui yhdellä otolla. Kuvauksen aikana huomasin, että olin rajannut kuvan hölmösti, kuvasin lähinnä puhujapönttöä, en Ainoa ja Miikaa. Tässä vaiheessa kello oli noin 10.05. Kun laulajat ja bändi valmistelivat esityksiään otin muutamia kuvia. Arvelin, että kuville voi olla käyttöä itse videossa.
Tiituksen flyygelillä soittama kappale meni ensimmäisellä otolla. Kello oli tässä vaiheessa 10.17, tajusin, että alkaa tulla kiire.
Karjalan kunnailla (2 ottoa), se englannikielinen kappale, jonka nimeä en muista ja Himlen i min famn ja Maammelaulu olivat tallennettuna 10.52. Näiden kuvan laatua en ehtinyt tarkistaa, sillä piti lähteä leikkaamaan. Vein Eriikan tuottaman ohjelman Jussille ja pyysin, että joku tekee aiheesta uutisen koulun sivuille.
leikkaus
Pääsin aloittamaan leikkaamisen noin 11.00. Siirsin videoklipit MacBookPro:lleni iMovie-ohjelmaan ja siirtelin ne paikoilleen. Tässä vaiheessa huomasin, että yksi laulutiedosto oli rikki, veikkaan, että koska akku oli ollut välillä aika tyhjänä, niin tiedosto oli vioittunut. Myös Maammelaulun tarkennus oli ikävästi pielessä. Lisäsin videon tilalle ottamiani valokuvia. Jokaisen klipin alusta ja lopusta leikkasin pois omat höpötykseni ja lisäsin siirroksen, häivytys mustaan. Rajasin Ainon ja Miikan puheen paremmaksi, tietäen että aiheuttaa videoon rakeisuutta. Lopuksi lisäsin alku- ja lopputekstit. Tässä vaiheessa kello oli 11.45. Tarkistin vielä kerran alun, siirroksien reunat ja lopun, korjasin pari ylimääräistä äännähdystä, jotka vielä olivat jääneet huomaamatta alkuperäisessä leikkauksessa. Video oli reilut puoli tuntia, iMovie tallensi sen mp4-tiedsotoksi alle 10 minuutissa.
youtube
Aloin siirtämään tiedostoa koulun YouTube-tilille. Kun kirjauduin sivulle, niin YouTube oli saksankielinen. Jos Hylliä etsi YouTuben sisältä, niin käyttöliittymä pysyi saksalaisena. Kun hain sanoilla youtube helsingin yhteislyseo, niin päädyin suomenkieliselle sivulle. Aloin tiedoston siirron. Tässä vaiheessa Jarno tuli auttamaan minua, sillä olin jo aika väsynyt. En meinannut löytää edes iMoviesta tallentamaani mp4-tiedostoa. Jarno myös tiesi, että kaikista YouTuben ilmoituksista ei pidä välittää. Noin 12.10 Jussi kuulutti, videon saatavuus voi hieman myöhästyä. Klo 10.14 saimme lopulta toimivan YouTube-linkin, jonka lähetimme koulun opettajille. Samalla linkki julkaistiin koulun vanhoilla (nämä näkyivät Helsingin opetusverkossa varmaankin jonkin välimuistin takia) ja uusilla kotisivuilla (minä pääsin ulkomaailmaan Freedomen avulla).
Klo 10.15 käytävältä alkoi kuulua videon ääniä. Peruskoulun opettajat näyttivät videon omilta koneiltaan oppilaille. Samalla tajusin, että olin unohtanut oman opetusryhmäni. Kun menin ryhmäni Meetiin, huomasin, että Jesse oli paikkaamassa poissaoloani, kiitokset siitä.
Tässä vaiheessa huokaisin helpotuksesta. Onnistuin.
opetuksia
Tämä ei olisi onnistunut, elleivät esiintyjät olisi valmistautuneet esityksiinsä huolella. Toisaalta se, että minulla oli mielessäni jonkinlainen suunnitelma aikataulusta ja pidin kiinni siitä, mahdollisti tuotoksen ajallisesti. Myös se, että niin moni henkilö toimi avustajana auttoi. Jos olen unohtanut mainita jonkun tuotantoon osallistuneen nimen, niin pyydän anteeksi.
Päätin, että en enää ikinä kuvaa ”virallisia” videokuvauksia ainakaan näillä välineillä. Meille pitää hommata asialliset kuvausvälineet, kouluttaa oppilaista kuvaajia ja leikkaajia. Kannattaisi hankkia myös tehokas Mac ja siihen Final Cut-ohjelma. Toki iMoviella pystyy helposti tekemään tällaisia projekteja, mutta tiedän iMovien rajallisuuden oikeissa isommissa projekteissa.
Kotona huomasin, että olin sittenkin siirtänyt videon omalle YouTube-tililleni. Tämä pitää korjata lähipäivinä. Jonkun hylliläisen somehenkilön pitää ottaa haltuun tilin hallinnointi.
Kirjoitin tämän lähinnä itselleni muistiinpanoksi. Ehkä tästä lukijakin voi jotain oppia.
Eri vaihtoehtojen lukumäärien laskeminen käsin on suhteellisen hidasta. Esitän tässä, miten käytin Python ohjelmointikieltä pienen käytännön ongelman ratkaisemiseen. Samalla kerron miten minun aivoni pikkuhiljaa kehittivät parempia ratkaisuja ongelman ratkaisuun.
Ongelman kuvailu
Ystäväni, kutsutaan häntä vaikka nimellä Jussi, pyysi minua auttamaan erään konferenssin järjestelyissä. Ilmoittautumisen yhteydessä osallistujilta oli kysytty mihin kahteen työpajaan he haluaisivat ilmoittautua kahdeksasta eri vaihtoehdosta. Konferenssitilassa on neljä huonetta, niinpä nuo kahdeksan työpajaa järjestetään siten, että ensin on samaan aikaan neljä rinnakkaista työpajaa ja sen jälkeen loput neljä. Työpajojen väliajalla osallistujat voivat tietysti vaihtaa huonetta. Jussi oli laskenut ilmoittautumisista, millaisia valintoja osallistujat olivat tehneet ja kuinka monta kutakin valintaa oli. Nyt Jussi halusi tietää, mikä olisi paras vaihtoehto eli kokousohjelma, järjestää nuo kahdeksan työpajaa neljän ryhmissä.
Jussi antoi minulle valinnat Excel-tiedostona, esitän tässä luvut Pythonin listoina muodossa [ensimmäinen valinta, toinen valinta, lukumäärä]. Kahdessa ensimmäisessä alkiossa olevat luvut ovat työpajojen numeroita 1, …, 8. Jos ensimmäinen luku on nolla, niin osallistuja oli valinnut vain yhden vaihtoehdon.
Kun sain kyseisen Excel-taulukon, otin avukseni kynän ja paperia. Hetken lukuja tuijotettuani, aloin järkeilemään suurin piirtein näin. Eniten on valintoja 1 ja 2. Niinpä niiden tulee olla eri aikaan.
1
2
Seuraavaksi eniten on 2 ja 7 eli 7 pitää olla eri puolella kuin 2.
1
7
2
Seuraavaksi eniten on 1 ja 7, mutta ne ovat jo taulukossa. Seuraavaksi on 1 ja 4, niinpä 4 tulee alemmalle riville.
1
7
2
4
Näin jatkaen päädyin taulukkoon
1
7
5
6
2
4
3
8
Laskin kuinka monta osallistujaa voi tällä tavoin järjestettynä osallistua haluamiinsa työpajoihin ja sain tulokseksi 98. Kokeilin muutamalla muulla eri tavalla ja en keksinyt parempaa valintaa.
Mieltäni jäi kuitenkin vaivaamaan, että jos sittenkin olisi jokin toinen kombinaatio, jossa saisi työpajat sopimaan suuremmalle osallistujamäärälle.
lukumäärän laskeminen
Päätinpä siis alkaa koodaamaan. Ensin laskin eri vaihtoehtojen lukumäärän. Samalla hahmottelin ohjelmaa päässäni. Jos raa’alla voimalla tekisi jokaisen mahdollisen ”lukujärjestyksen”, niin niitä tulisi olemaan 8! = 40320. Toki koneella laskien tuo onnistunee nopeasti, mutta ohjelmoidessa saa ja tulee käyttää myös järkeä.
Loppujen lopuksihan on sama missä järjestyksessä kumpikin rivi on. Ensimmäisen rivin työpajat voivat olla jälkimmäisenä ja toisen rivin ensimmäisenä. Toisaalta ensimmäisen rivin jäsenet voivat olla missä järjestyksessä tahansa. Tällä tavoin ajatellen jos valitaan ensin kaikki mahdolliset neljän mittaiset kombinaatiot, niin näitä tulee nCr(8, 4) = 70 kappaletta ja jos jokaiseen liitetään jäljellä olevista luvuista permutaatiot joita on 4! = 24, niin eri vaihtoehtoja tulee olemaan 70*24 = 1680 kappaletta.
python koodin eka suunnitelma
Tein mielessäni ja paperilla suunnitelman Python ohjelmasta.
tuota vaihtoehdot
luo permutaatiot ekaksi riviksi taulukossa
määritä jäljelle jäävä nelikko joukko-opillisena erotuksena
luo kombinaatiot alemmaksi riviksi
yhdistä yhdeksi listaksi
määrät listaksi
luo summa muuttuja
laske määrät summaan
tähän tulee hankala if-lause
luo tuloslista
Avasin Anaconda/Spyder-ohjelmoitiympäristön. Googlettelin aika paljon, jotta löysin tarvittavat apuohjelmat ja komennot, mutta muutamassa tunnissa ohjelma tuotti 1680 tulosta, joiden avulla pystyi valitsemaan parhaat aikataulut. Hankalin vaihe oli keksiä tarvittava if-lause, jonka avulla sai pääteltyä, pääseekö molempiin valitsemiinsa ohjelmiin. Alla on kyseinen for-silmukka, jolla varsinainen lasku suoritettiin.
for j in range(1680): for li in maarat: if(((li[0]==0 or (li[0] in listojenlista[j][0])) and (li[0]==0 or (li[1] in listojenlista[j][1]))) or ((li[0]==0 or (li[1] in listojenlista[j][0])) and (li[0]==0 or (li[0] in listojenlista[j][1]))) ): summa[j]+=li[2]
Oma alkuperäinen kynällä ja paperilla tuotettu aikatauluni oli tasapisteissä parhaana. Niinpä lähetin eri vaihtoehtojen lukumäärät Jussille. Tehtävä suoritettu.
aivotoimintaa
Jotenkin mieleeni jäi kaihertamaan, että olisi tuon voinut tehdä elegantimminkin. Pari päivää myöhemmin illalla, aloin pohdiskelemaan ongelmaa ja vilkaisin tuottamaani koodia. Ajattelin, että tuo if-lausehirvitys pitäisi sieventää nätimmäksi ihan harjoituksen vuoksi. Mutta sitten telkkarista tuli jotain tärkeää ja unohdin asian. Seuraavana yönä heräsin ja tajusin, että koko jutun voi tehdä paljon helpommin.
Aikataulunhan määrää nuo ensimmäisen rivin luvut. Toisella rivillä ovat ne luvut, jotka eivät ole ensimmäisellä rivillä. Näin tutkittavia listoja tulee olemaan vain 70 kappaletta. Kirjoitin ajatukseni muistiin aamulla, mutta en palannut asiaan, kun oli kaikkia muita kiireitä, pitäähän opettajan käydä koululla opettamassa. Pari päivää myöhemmin aamusuihkussa oivalsin yksinkertaisemman tavan, miten if-lause kannattaa kirjoittaa. Ihmisen aivot toimivat kummallisesti.
lopullinen koodi
Näytän alla lopullisen koodin ilman kommentointia, kommentoin rivejä koodin lopussa.
rivit 1-5: Spyderin tuottamaa metatietoa
rivi 7: Goolettamalla löysin tiedon, että kombinaatiot saa tuotettua itertools-kirjaston combinations funktiolla.
rivi 9: range(1,9,1) tuottaa listan [1, 2, …, 8], combinations(range(1,9,1),4) tuottaa näistä listan, jossa ovat kaikki 70 kombinaatiota tyyliin [(1, 2, 3, 4), (1, 2, 3, 5), …, (4, 6, 7, 8), (5, 6, 7, 8)].
rivi 10: map(list, ekattuplet) muuntaa monikot (tuplet) (1, 2, 3, 4) listoiksi. Combinations-komennon jäljiltä edellä esitetty lista on tyypiltään iterable (mitähän tämä on suomeksi), se muuttuu todelliseksi listaksi tyyliin [[1, 2, 3, 4], …, [5, 6, 7, 8]] list-funktiolla. Map on saman tyyppinen komento kuin GeoGebran Zip, sillä saa toistettua komennon syötelistan jokaiselle alkiolle. Koodin olisi saanut toimimaan, vaikka listan alkiot olisivat olleet monikkoja, mutta halusin testata map-komentoa :o)
rivi 11-14 Valinnat ja vastaavat määrät, kuten luvussa ”Ongelman kuvailu” on esitetty. Kenoviiva katkaisee rivin.
rivit 20-21: Sisäkkäiset silmukat, jossa käydään läpi jokainen ekatlistan alkio ja lasketaan niille kullekin, miten maaratlistan valinnat sopivat.
rivi 22-23: Jos maarat-listassa on 0, niin valinta kelpaa, lisätään osallistujien määrä summaan.
rivi 24-27: Varsinainen ehtorivi. Tässä selvitetään, onko vain jompikumpi valinnoista ekassa listassa. Ehtolause on ehkä hieman paremmin luettavissa, jos sen rivittää eri riveille
rivit 29-31: Yhdistetään ekatlistan järjestykset ja niitä vastaavat summat yhdeksi, jotta ne saa järjestettyä suuruusjärjestykseen. Metodi append lisää listan perään alkioita.
rivi 33: Järjestetään tuloslista summien mukaiseen järjestykseen.
rivit 35-36: Tulostetaan lopputulos.
Koodin tuottama lopputulos on tämän tarinan lopussa.
jatkopohdintaa
Tällaiset pienet ongelmat pitävät ainakin minun aivoni virkeinä. Tätä kirjoitettaessa tuli mieleen, parikin uutta pohdinnan aihetta. Mitä jos kaikki 148 osallistujaa olisivat tehneet valintansa satunnaisesti, mikä tällöin olisi ollut parhaan aikataulun osallistujien lukumäärä ja vastaavasti heikoimman. Pitänee tehdä simulointi tai sitten yrittää laskea todennäköisyyslaskennan avulla.