Jääkiekon MM-kisojen turnausformaatti yksinkertaisimmassa muodossaan. Koodilla voi testata sitä, kuinka hyvin pistelasku toimii, ja kuinka paljon se sietää epävarmuutta.
Joukkueet ovat vain kokonaislukuja, jotka edustavat niiden vahvuustasoja. Ottelut ratkaistaan normaalijakauman mukaan tuotetuilla satunnaisluvuilla. (random.gauss -funktio)
Olen käyttänyt lähes jokaisen funktion käytössä sorted-funktion kaltaista käyttöliittymää: Funktiolle syötetään tupleja vapaassa järjestyksessä, ja se palauttaa ne järjestettynä. Poikkeuksena ottelufunktion argumentit, joille joukkueet syötetään erikseen. Esim. pronssiottelu-muuttuja sisältää järjestetyn listan pronssiottelun tuloksista.
Koodirivejä on vain reilut 50. Onko tämä liiallista yksinkertaistamista?
Omien kokeiden perusteella otteluformaatti toimii hyvin, ja ainoa haaste on joukkueiden pelikohtainen varianssi.
Lisäinfoa projektista löytyy tuolta:
http://digitaalinenilluusio.blogspot.fi/2016/05/mm-jaakiekon-turnausformaatin.html
# -*- coding: utf-8 -*- import random, itertools #Suorittaa yksittäisen ottelun #Palauttaa tuplen, jossa (voittaja, häviäjä) def pelaa_ottelu_sorted(joukkue1, joukkue2): if(random.gauss(joukkue1 - joukkue2, 0.5) < 0): return (joukkue2, joukkue1) else: return (joukkue1, joukkue2) #Suorittaa lohkolle alkusarjan pelit #Palauttaa tuplen, jossa lohkon joukkueet järjestettynä pisteiden mukaisesti def pelaa_alkusarja_sorted(lohko): pistetaulu = [0]*8 for (id1, jo1), (id2, jo2) in itertools.combinations(enumerate(lohko), 2): if pelaa_ottelu_sorted(jo1, jo2)[0] == jo2: pistetaulu[id2]+=3 else: pistetaulu[id1]+=3 #Järjestetään pisteiden perusteella return [x for (y, x) in sorted(zip(pistetaulu, lohko), reverse=True)] #Toteuttaa turnauksen #palauttaa neljä parasta joukkuetta, järjestettynä sijoituksen mukaan voittajasta aloittaen def turnaus_sorted(joukkueet): lohkoA = pelaa_alkusarja_sorted(joukkueet[0:8]) lohkoB = pelaa_alkusarja_sorted(joukkueet[8:18]) puolivaliera1 = pelaa_ottelu_sorted(lohkoA[0], lohkoB[3]) puolivaliera2 = pelaa_ottelu_sorted(lohkoA[2], lohkoB[1]) puolivaliera3 = pelaa_ottelu_sorted(lohkoB[0], lohkoA[3]) puolivaliera4 = pelaa_ottelu_sorted(lohkoB[2], lohkoA[1]) valiera1 = pelaa_ottelu_sorted(puolivaliera1[0], puolivaliera2[0]) valiera2 = pelaa_ottelu_sorted(puolivaliera3[0], puolivaliera4[0]) finaali = pelaa_ottelu_sorted(valiera1[0], valiera2[0]) pronssiottelu = pelaa_ottelu_sorted(valiera1[1], valiera2[1]) #jarjestetaan lopullinen ranking tuloslista = finaali + pronssiottelu return tuloslista joukkueet = range(16) random.shuffle(joukkueet) print "Kaikki joukkeet:", joukkueet print "Neljä parasta: ", turnaus_sorted(joukkueet)
Nyt en ymmärrä, mikä tästä tekee mielestäsi koodivinkin. Siirrän keskusteluun.
Mikä siitä mielestäsi ei tee koodivinkkiä?
Oletko täysin järjissäsi? Millä oikeudella leikit täällä ylläpitäjää?
Todella turhauttavaa sinun toimintasi. Jatkuvaa inttämistä keskustelussa, käyttäjätilini poistettiin ja sitten tämä. Olen kirjoitellut koodivinkkejä jo vuonna 2003, ja ne olivat pidettyjä.
Voisitko selittää?
jsbasic kirjoitti:
Mikä siitä mielestäsi ei tee koodivinkkiä?
Jää täysin epäselväksi, mitä tuosta koodista olisi tarkoitus oppia. Hieno juttu, osaat laittaa taulukon satunnaiseen järjestykseen ja simuloida muutamaa ”ottelua”. Entä sitten? Tässä ei vielä ole ohjelmoinnin kannalta mitään kiinnostavaa.
Normaalijakaumasi on ihan hatusta vedetty. Osaatko edes selittää, miksi vertailu menee noin ja millaisia todennäköisyyksiä siitä tulee?
”Vinkissä” esität myös itse kysymyksen (”Onko tämä liiallista yksinkertaistamista?”), miksi? Koodivinkin pitäisi antaa vastauksia eikä kysymyksiä.
jsbasic kirjoitti:
käyttäjätilini poistettiin
Mikä käyttäjätilisi nyt on poistettu? Tässähän sinulla on käyttäjätili.
jsbasic kirjoitti:
Olen kirjoitellut koodivinkkejä jo vuonna 2003, ja ne olivat pidettyjä.
Onneksi ohjelmoinnin taso on noussut vuodesta 2003. Myöskään ”pidetty” vinkki ei ole sama asia kuin hyvä vinkki. Olet lähettänyt näköjään vain pari vinkkiä, ja yhdessäkin on tällainen hieno kommentti koodissa: ”Tässä on joitain vakioita (500, 50 ja 3) joita en oikein ymmärrä.” Kuulostaako hyvältä vinkiltä?
En aio kirjoittaa ammattimaisia vinkkejä ilmaiseksi. Eikä Suomesta montaa sellaista löydy, jotka tekisivät sitä sinulle. Tuskin täällä muuten olisi niin hiljaista nykyisin...
Mielestä tämän Putkan idea oli, että kaikki voi ohjelmoida. Laaksonen kirjoitti alussa hurjasti BASIC-vinkkejä, ja edelleen GOTO-käsky näkyy logossa. Se ei anna hirveän ammattimaista kuvaa 2010-luvulla. ;)
Sen sijaan koodissani oli pythonin huippukehittynyt iteraattori, joka hakee kombinaatioita itemien kesken. Ja gaussin satunnaisgeneraatio.
No, linkittämästäni blogista löytyy lisäinfoa...
jsbasic kirjoitti:
Mielestä tämän Putkan idea oli, että kaikki voi ohjelmoida.
Tietenkin saa ohjelmoida. Koodivinkin julkaiseminen on eri asia kuin ohjelmointi. Satunnaisia juttuja voi aivan hyvin lähettää keskusteluun. Jos taas on tarkoitus kehittää jotain ideaa pidemmällekin ja tehdä koodista uusia versioita, projektialue voi olla siihen otollisempi paikka. Koodivinkit ovat oppimista varten, ja sieltä löytyy helpommin oikeaa asiaa, jos ei tungeta sinne lisää epäselvyyksiä. Toki vinkeissä on myös kategoria ”omat tuotokset”, mutta silloin ohjelmasta saisi mielellään saada ulos muutakin kuin 16+4 lukua osittain satunnaisessa järjestyksessä.
jsbasic kirjoitti:
En aio kirjoittaa ammattimaisia vinkkejä ilmaiseksi.
Jos ei kiinnosta kirjoittaa hyvää ja perusteellista vinkkiä, älä sitten kirjoita vinkkiä. Mitään ei menetetä, koska huono vinkki ei ole juuri minkään arvoinen.
Ammattimainen on myös eri asia kuin hyvä.
jsbasic kirjoitti:
Sen sijaan koodissani oli pythonin huippukehittynyt iteraattori, joka hakee kombinaatioita itemien kesken. Ja gaussin satunnaisgeneraatio.
Et selitä näitä mitenkään etkä kuvaile näiden järkevää käyttöä, joten ei voi sanoa, että vinkkisi kertoisi näistä. Erityisesti normaalijakauman suhteen jää täysin epäselväksi, mitä edes yritetään saada aikaan. Jos 50 rivistä 1–2 riviä on asiaa, suhde ei ole vielä kovin vakuuttava.
jsbasic kirjoitti:
No, linkittämästäni blogista löytyy lisäinfoa...
Siinäpä se! Onko tarkoitus läntätä koodivinkkeihin melko epämääräisesti selitetty koodi ja ohjata muualle lukemaan lisää, vai onko tarkoitus selittää koodivinkissä asiat kunnolla? Jos kaikki tuo lisäinfo olisi ollut koodivinkissä, olisi voinut olla jotain toivoa, tosin selitykset ovat blogissakin mielestäni todella epämääräisiä ja ideat heikosti perusteltuja.
En itsekään ymmärrä mitä vinkki koskee. Vinkin tulisi olla ratkaisu tai toteutusmalli yleisluonteiseen ongelmaan, ei "näin teet lottogenerattorin" -tyylinen snippet.
Voihan lottogeneraattorinkin laittaa vinkiksi jos se on perustellusti toteutettu.
Tuollaiset että "selitetty blogissa" on sikäli hankalia, että sitten kun blogi on jossain vaiheessa bittiavaruudessa niin vinkistä tulee vaan 404:ää.
En kirjoittanut koodia alunperin vinkiksi, vaan testasin pythonin ilmaisukykyä. Mutta tein sittemmin yksinkertaistetun version, joka on mielestäni suhteellisen kaunis. Jaoin sitä joillekin nettisivuille, kuten tänne - tosin kullekin sivustolle eri muodoissaan.
Tarkoitus oli, että koodi puhuu puolestaan. En julkaisuvaiheessa kiinnitänyt tekstiin suurta huomiota, koska on epävarmuutta siitä, että onko tämä Putka oikea kohderyhmä. Onko täällä niitä, jotka kaipaavat tälläistä vinkkiä. Editointimahdollisuushan täällä on, jos tämä olisi vinkinksi päätynyt.
jsbasic kirjoitti:
#Suorittaa lohkolle alkusarjan pelit #Palauttaa tuplen, jossa lohkon joukkueet järjestettynä pisteiden mukaisesti def pelaa_alkusarja_sorted(lohko): pistetaulu = [0]*8 for (id1, jo1), (id2, jo2) in itertools.combinations(enumerate(lohko), 2): if pelaa_ottelu_sorted(jo1, jo2)[0] == jo2: pistetaulu[id2]+=3 else: pistetaulu[id1]+=3 #Järjestetään pisteiden perusteella return [x for (y, x) in sorted(zip(pistetaulu, lohko), reverse=True)]
Alkusarjan otteluilla on lupa päättyä tasan, jolloin koodisi on tältä osin puutteellinen. Lisäksi kotiottelun merkitys ja muut peliin liittyvät asiat (loukkaantumiset, pelikiellot) ynnä muut sellaiset loistavat poissaolollaan. Sinällään ihan näppäriä rakenteita kielessä kieltämättä on, mutta niiden käytölle pitäisi löytyä paremmin selitettyjä perusteita.
Kiitos palautteesta. Nuo MM-kisojen alkusarjapelit pelataan nykyisin ratkaisuun asti. Tosin, jos ratkaisu ei tule varsinaisella peliajalla, hyvitetään hävinneelle yksi piste. Ottelu voi pisteiden osalta päättyä siis neljällä eri tavalla. Lisäksi sijoitus alkusarjassa, ja pudotuspeleista pudonneiden lopullinen sijoitus turnauksessa määräytyy monien sääntöjen mukaan. Tässä koodissa ei sellaisia ole.
Käyttäisin joukkueen tietorakenteena dictiä tuplen tilalla. Tällöin epäselvien indeksien sijasta voidaan käyttää kuvaavia merkkijonoja. Tiedon hallinta helpottuu muutenkin.
Esimerkki:
joukkue = {'nimi': 'Suomi', 'pisteet': 0}
Tuolla rakenteella saat joukkueen kaikki tiedot yhden muuttujan taakse.
Aina on tietysti mahdollista, että nälkä kasvaa syödessä eli lisäät yksityiskohtia ja jossain vaiheessa joukkueesta onkin parasta tehdä oma luokkansa. Dict on kuitenkin hyvä ja kevyt perusrakenne tuohon, miltä toteutus nyt näyttää, pieni laajennusvara huomioiden.
Chiman kirjoitti:
Käyttäisin joukkueen tietorakenteena dictiä tuplen tilalla. Tällöin epäselvien indeksien sijasta voidaan käyttää kuvaavia merkkijonoja.
Ohjelmassani joukkueen numero ja vahvuustaso ovat sama asia. Niinhän ei voi olla asian laita, jos ohjelmaa haluaa laajentaa. :)
Erillisen pistetaulun käyttäminen on minusta sen sijaan perusteltua. Tuo enumeraten ja sortedin käyttö on varmaan koodin rumin kohta, mutta se ei näy funktion ulkopuolelle.
Hahmottelin vaihtoehtoisen toteutuksen tuolle alkusarjan funktiolle, tosin testaamatta:
# Suorittaa lohkolle alkusarjan pelit # Palauttaa tuplen, jossa lohkon joukkueet järjestettynä pisteiden mukaisesti def pelaa_alkusarja_sorted(lohko): pistetaulu = dict(zip(lohko, [0] * len(lohko))) for jo1, jo2 in itertools.combinations(lohko, 2): voittaja = pelaa_ottelu_sorted(jo1, jo2)[0] pistetaulu[voittaja] += 3 # Järjestetään pisteiden perusteella return [j for j in sorted(lohko, key=lambda x: (pistetaulu[x], x), reverse=True)]
Eli pistetaulu dictinä. Hyvin oivallettu!
Suunnittelin semmosta, että lohko järjestettäisiin jo tuossa samassa silmukassa, jossa pisteetkin jaetaan. Silloin sorted-funktiota ei tarvittaisi.
Olen myös yrittänyt jakaa noita alkusarjan pelejä kierroksiin. Tällä hetkellä ensimmäinen joukkue joutuu pelaamaan kaikki pelinsä peräkkäin. Tehtävä on melkoinen puzzle, johon en ole vielä algoritmia keksinyt.
jsbasic kirjoitti:
Suunnittelin semmosta, että lohko järjestettäisiin jo tuossa samassa silmukassa, jossa pisteetkin jaetaan. Silloin sorted-funktiota ei tarvittaisi.
Todennäköisesti tuosta tulisi sekä monimutkaisempi että hitaampi ratkaisu. En suosittele.
lainaus:
Olen myös yrittänyt jakaa noita alkusarjan pelejä kierroksiin. Tällä hetkellä ensimmäinen joukkue joutuu pelaamaan kaikki pelinsä peräkkäin. Tehtävä on melkoinen puzzle, johon en ole vielä algoritmia keksinyt.
Tuossa on eräs ratkaisu siihen:
https://en.m.wikipedia.org/wiki/Round-robin_tournament
jsbasic kirjoitti:
Olen myös yrittänyt jakaa noita alkusarjan pelejä kierroksiin. Tällä hetkellä ensimmäinen joukkue joutuu pelaamaan kaikki pelinsä peräkkäin. Tehtävä on melkoinen puzzle, johon en ole vielä algoritmia keksinyt.
Kumma juttu, mutta joku on jaksanut tehdä ilmaiseksi koodivinkin aiheesta täyskierrosturnauksen paritus.
"Round-robin scheduling" on näköjään se avainsana. MM-kisaohjelman luomiseen käytetään sen yhteydessä varmasti muitakin algoritmeja...
Mutta tarkoitus oli testata Venäjän kisojen toimivuutta. Siksi kopioin niiden sarjaohjelman -- päivien mukaan.
lohkoOhjelma=( ((1,4),(0,2)), ((3,7),(5,6),(4,2)), ((7,0),(5,3),(1,6)), ((4,0),(1,2)), ((3,6),(7,5)), ((3,4),(1,7)), ((2,5),(0,6)), ((2,7),(6,4)), ((5,1),(0,1),(7,4)), ((6,2),(3,1)), ((0,5),(6,7)), ((2,3),(4,5),(0,1)) )
Ja sitten...
for paiva in lohkoOhjelma: for (koti, vieras) in paiva: pelaa_ottelu_sorted(lohko[koti], lohko[vieras])
...ja niin voin simuloida vaikka joukkueiden väsymystä, joka aiheutuu peräkkäisistä peleistä. Voisi odottaa, että mitä samantasoisempia joukkueet ovat, sitä enemmän väsymystä aiheutuu.
Tulipa mieleen tämä normaalijakauma-asia, kun pelataan World Cupia.
Metabolix kirjoitti:
Normaalijakaumasi on ihan hatusta vedetty. Osaatko edes selittää, miksi vertailu menee noin ja millaisia todennäköisyyksiä siitä tulee?
Asia on selitetty Wikipediassa, mutta se avautuu paljon helpommin visuaalisesti. Kannattaa hakea videohaulla Probability Machinea.
Normaalijakauma tarkoittaa lyhyesti ilmaistuna satunnaislukujen keskiarvon jakautumista. Yleensähän on se ilmiö, että mitä enemmän satunnaislukuja on keskiarvoon laskettu, sitä "keskemmälle" se hakeutuu. Siksi gaussinkäyrä on korkea keskeltä.
Oletin algoritmissani, että jääkiekkopeli on "sattumien summa" siinä merkityksessä, että jokainen peliminuutti tasoittaa lopullista tulosta. Vastaavasti jokainen pelaaja tasoittaa tulosta. Siksi en laskisi joukkueen onnistumista yhden satunnaisluvun, vaan useiden satunnaislukujen keskiarvon mukaan. (Algoritmini laski nimenomaan onnistumista.)
Toisaalta jääkiekossa (ja etenkin jalkapallossa) tulos ratkaistaan muutaman maalin perusteella. Jokaisella minuutilla ei tehdä maaleja, jokainen pelaaja ei niitä tee (tarkoitan eniten ratkaisevaa pelaajaa kyseiselle maalille enkä sitä, jolle tarjoiltiin namupassi). Siten pelissä voisi olettaa olevan "turhia minuutteja ja pelaajia", jotka jäävät lopputuloksessa ottelun kulmakivien varjoon.
Usein vedotaan psykologiaan: Joukkueella on itseluottamusta tai on momentum-ilmiö. Kuitenkin, muutama yksittäinen maali (tai vastustajan hukattu maali) voi aiheuttaa voittoputken, jolle haetaan selitystä kaikista pelaajista. Kyse voi olla kuitenkin muutamasta peräkkäisestä onnekkaasta pompusta.
Siis, jos jääkiekko-ottelun tuloksilla on jonkinlainen todennäköisyysjakauma, niin millainen se on?
jsbasic kirjoitti:
Asia on selitetty Wikipediassa – –
Onneksi en kysynytkään, mikä on normaalijakauma, vaan kysyin, oletko itse tietoinen, millaisia todennäköisyyksiä tuosta kaavasta tulee. Tosin alussa tavallaan vastasitkin jo kysymykseen:
jsbasic kirjoitti:
Omien kokeiden perusteella otteluformaatti toimii hyvin, ja ainoa haaste on joukkueiden pelikohtainen varianssi.
Eli haaste on, että käyttämäsi jakauma ei vastaa otteluiden tulosten todellista (tai simulaatiossa toivottavaa) jakaumaa.
Kuvaat joukkueen hyvyyttä kokonaisluvulla. Silloin pienin mahdollinen joukkueiden ero on 1. Silloin käyttämäsi kaava antaa huonommalle noin 2 %:n mahdollisuuden voittaa. Minusta se on aika vähän. Jos joukkuiden ero on 2, huonomman voittomahdollisuus on enää sadastuhannesosia eli aivan olematon. Näillä lukemilla ottelu ei nyt todellakaan ole ”sattumien summa”, vaan lopputulos käytännössä määräytyy ennalta asetetuista joukkueiden hyvyysluvuista.
Kuulostaisi selvemmältä (ja enemmän sinunkin sanallista selitystäsi vastaavalta) määrittää kummankin joukkueen onnistuminen normaalijakauman avulla ja verrata sitten näitä onnistumisia pelin lopputuloksen määrittämiseksi:
# Joukkueen onnistuminen voi noudattaa normaalijakaumaa: # yleensä menee oman tason mukaan, joskus paremmin, joskus huonommin def onnistuminen(hyvyys, vaihtelu): return random.gauss(hyvyys, vaihtelu) # Yksinkertainen käyttöesimerkki nykyisessä koodissasi, # jossa ei vielä ole joukkuekohtaista vaihtelua (-> laitetaan 1) mutta # on kuitenkin joukkuekohtainen hyvyys tiedossa: def pelaa_ottelu_sorted(joukkue1, joukkue2): hyvyysluvun_kerroin = hyke = 0.4 if (onnistuminen(joukkue1 * hyke, 1) < onnistuminen(joukkue2 * hyke, 1)): return (joukkue2, joukkue1) else: return (joukkue1, joukkue2)
Tuosta voi onnistumisten erotuksen perusteella päättää vaikka ottelun maalimäärän niin, että isompi onnistuminen tuo enemmän maaleja.
Metabolix kirjoitti:
Eli haaste on, että käyttämäsi jakauma ei vastaa otteluiden tulosten todellista (tai simulaatiossa toivottavaa) jakaumaa.
Tarkoitin turnauksen formaattia, joka on IIHF:n ylläpitämä järjestelmä. Omassa koodissani formaatti on identtinen IIHF:n kanssa. (Lukuunottamatta sitä, että muodostan alkulohkot satunnaisesti, kun taas IIHF:n järjestelmässä ne järjestyvät rankingin ja monimutkaisten sääntöjen mukaan.)
Tuo 2% on siis se määrä, jonka tuo MM-kisaformaatti sietää satunnaisuutta. Todellisuudessa satunnaisuus on paljon korkeampi, ja siksi huonompikin joukkue voi voittaa mestaruuden. Turnaukset ovat viihdettä -- eivät tieteellinen koe.
Aihe on jo aika vanha, joten et voi enää vastata siihen.