Skandináv lottó előállítása változókkal és rekurzióval

Újra skandináv lottó (heteslottó) szelvényt generálunk Java programmal. Két különböző elven megvalósuló megoldást hasonlítunk össze. A megoldást lépésenként bemutató, grafikus felhasználói felületű, saját fejlesztésű programunkról blogoltunk már: Skandináv lottó demóprogram. Most másképpen gondolkodunk – reméljük közösen.

Építünk az alábbi tipp() függvényre:

Ennek a függvénynek lokális felelőssége van, azaz előállít egyetlen olyan véletlenszámot, ami megfelelő a skandináv lottószelvényre. Másképpen 1 és 35 közötti (zárt intervallum) egész szám.

A globális felelősség a megoldások során azt biztosítja, hogy minden szám egyedi/különböző legyen és 7 db véletlenszám legyen.

1. megoldás

Az első megoldás során csupán egyszerű változók jelentik az emlékezetet. Külön adatszerkezet (tömb, lista, halmaz) nincs. A lotto1() metódus valósítja meg az alábbiak szerint:

Az a egész ( int) típusú változó ellenőrzés nélkül előállítható, mert az első szám biztosan egyedi. A b változót csak az a-val kell összehasonlítani. Célszerű a do while hátultesztelő ciklus alkalmazása. A b-t addig tippeltetjük újra és újra, amíg szükséges. Másképpen: amíg a megegyezik b-vel ( a==b), addig nem jó a b és ezért újra kell generálni. A c változó esetében már a korábban előállított a-val és b-vel is össze kell hasonlítani, hiszen c-nek mindkettőtől különböznie kell. Másképpen (1): amíg a==c vagy b==c, addig nem jó a c és ezért újra kell generálni. Másképpen (2): amíg nem igaz, hogy a különbözik c-től és b is különbözik c-től, addig meg kell ismételni a c előállítását. Kiválóan megérhető a gondolatmenet alapján a megoldás során használt De Morgan azonosság. És így tovább a többi változóval is: d, e, f, g. Nehézkes a sok változó külön-külön való kezelése és figyelmesen kell megírni Java nyelven a hosszú forráskódot. Az eredmény a konzolon jelenik meg, például így:

Kompromisszum, hogy a lottószelvényre kerülő véletlenszámok sorrendje is véletlen. Másképpen: a véletlenszámok nem „emelkedő számsorrendben” jelennek meg.

2. megoldás

Most nem használunk ciklus utasítást. Persze a lottószelvényre kerülő véletlenszámok előállítása megköveteli az ismétlést a vezérlés megvalósítása során. Az ismétlést rekurzív vezérlés oldja meg. Az emlékezetet most TreeSet jelenti. Ez egy generikus, Integer típusú adatszerkezet, amely a java.util csomagbeli kollekció keretrendszer része. Rendezett halmazként működik. Cím szerinti paraméterátadással kapja meg a metódus és kezdetben üres. Addig kell beletenni újonnan előállított véletlenszámot, amíg „egyszer csak” eléri az elemszáma a 7-et. Ha olyan elem kerül a halmazba, ami már benne van, akkor az add() halmazbővítő művelet hatástalan.

A rekurzió során a lotto2() függvény saját magát hívja meg. Működése során kihasználja, hogy az adatszerkezet „menet közben” megváltozik (bővül). A lottószelvényre kerülő véletlenszámok sorrendje egyfajta mellékhatásként valósul meg, mert a TreeSet típusú kollekció automatikus képessége. Az eredmény a konzolon jelenik meg, például így:

Ha érdekelnek bennünket a részletek, akkor a rekurzív vezérlés kiegészíthető a lotto3() metódusnál látható szint paraméterrel és néhány kiírással az alábbiak szerint:

Az önmagyarázóvá tett forráskód például az alábbi eredményt írhatja ki a konzolra:

Jól látható, hogy mennyi lépést igényel a lottószelvény előállítása, mikor fordult elő ismétlődés a véletlenszámok előállítása során.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 9-12. óra: Metódusok, rekurzió alkalomhoz, illetve a 21-24. óra: Objektumorientált programozás, 2. rész alkalomhoz kötődik.

Születésnap-paradoxon

Mennyi a valószínűsége, hogy n ember között van kettő, akiknek egy napon van a születésnapja? A meglepő a dologban az, hogy már 23 ember esetén a kérdéses valószínűség 1/2-nél nagyobb. Másképpen: már 23 ember esetén nagyobb annak az esélye, hogy megegyezik a születésnapjuk, mint az ellenkezőjének. Ez a 23 nagyon kevésnek tűnik. Ezért paradoxon.

Közismert néhány hétköznapi valószínűség. Íme néhány szabályos eset. A pénzfeldobás során 1/2 az esélye a fej és 1/2 az esélye az írás eredménynek (másképpen 50%-50%, azaz fifty-fifty). A kockadobás esetén 1/6 az esélye bármelyik számnak 1-től 6-ig. Két kocka esetén blogoltam már a dobott számok összegének alakulásáról, eloszlásáról: Kockadobás kliens-szerver alkalmazás.

Néhány egyszerűsítés

  • Az év 365 napból áll. Nem számítanak a 366 napos szökőévek.
  • A születések eloszlása egyenletes, azaz minden nap körülbelül ugyanannyian születnek. Nem számít, hogy hétköznap, hétvége, ünnepnap. Az áramszüneti városi legendák sem.
  • Nem vesszük figyelembe az azonos napon született ikreket. Persze ikrek születhetnek különböző napokon is.

Azonos születésnap valószínűsége grafikonon

Lássuk, hogyan alakul az azonos születésnap valószínűsége az emberek számától függően! Grafikonon ábrázolva:

A fenti grafikonhoz szükséges adatok könnyen előállíthatók az alábbi Java forráskóddal:

A fenti Google Chart típusú szórásgrafikon (Scatter Chart, korrelációs diagram) megjelenítéséhez adatpárok sorozata szükséges. Ezek a konkrétumok (70 db adatpár), görgethető:

Hasonló grafikon készítéséről szintén blogoltam már: Céline Dion – Courage World Tour.

Párok előállítása

Az emberek születésnapjainak összehasonlítása párokban történik. 23 ember esetén 23*22/2=253 pár van. Általános esetben n ember esetén (n*(n-1))/2 pár adódik. A levezetés részletei a források között megtalálható. 59 ember esetén 1711 pár adódik és szinte garantált az előforduló azonos születésnap, hiszen már 0,99 ennek a valószínűsége.

Az alábbi Java forráskód – rekurzív módon – előállítja a 23 konkrét esetre a párokat, az embereket 1-23-ig sorszámozva. Kombinációk:

A main() metódusban az i változó paraméterezhető és a konkrét eset könnyen intervallumra változtatható. Eredményül ezt írja ki a program a konzolra, görgethető:

Kísérleti ellenőrzés

Tekintsünk például 1000 esetet! Készítsünk Java programot, amely 23 db véletlen születésnapot generál! Legyen ez a születésnap sorszáma az évben (másképpen hányadik napon született az ember az évben). Ez lényegesen egyszerűsíti a megoldást, összevetve a dátumkezelésen alapuló megközelítéssel. Ajánljuk a szakmai blog dátumkezelés címkéjét az érdeklődőknek, ahol megtalálhatók a témához kapcsolódó Java forráskódrészletek részletes magyarázatokkal kiegészítve. Íme a többféle generikus listát és programozási tételt használó forráskód:

Érdemes elemezni, tesztelni a fenti forráskódot: milyen lépésekben, milyen adatszerkezeteket épít. Hasznos lehet lambda kifejezésekkel kiegészíteni, módosítani a programot. Részlet a program szöveges eredményéből:

A 12. sorban lévő számhármasok jelentése: esetszám 1-től, azonos nap, előfordulás száma. Például: a kísérlet során a 8. esetben az év 225. napja azonos 3 embernél. Természetesen nincs garancia arra, hogy az 1000 eset vizsgálatánál mindig 500-nál nagyobb kedvező esetet kapunk.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 17-28. óra Objektumorientált programozás alkalmaihoz kötődik.

Források

Beszámoló: it-tanfolyam.hu STEM nyári tábor 2023

A STEM mozaikszó eléggé közismert: a tudományos-technológiai tudományágakat (természettudomány, technológia, mérnöki tudomány és matematika) foglalja egybe, interdiszciplináris megközelítésben. A STEM területén való elmélyedés során a hangsúly nem a mit tanulunk/tanítunk, hanem inkább a hogyan tanulunk/tanítunk. Nem azonnal ad kézzel fogható válaszokat, de kitartó próbálkozással – saját élménnyel – elérhető az eredmény.

Az it-tanfolyam.hu oktatói csapata 2023-ban először hirdetett STEM nyári tábort. Erről számolunk be röviden ebben a blog bejegyzésben. Tervezzük, hogy a jövőben rendszeresen fogunk szervezni STEM nyári tábort.

A STEM nyári tábor koncepciója

2023. nyarán 4 turnusban hirdettünk programozás fókuszú STEM nyári tábort:

  • 1. turnus: július 3-7-ig,
  • 2. turnus: július 10-14-ig,
  • 3. turnus: július 17-21-ig,
  • 4. turnus: július 24-28-ig.

Előzetes tudás- és igényfelmérést végeztünk, így alakítottunk ki 3 db csoportot, ezek: Java kezdő, Python kezdő, Python haladó. A kiinduló célcsoportot tanfolyamaink karrierváltó hallgatóinak gyermekei jelentették, akik mellé toboroztunk még. A korosztály a 16-20 éves diákok voltak a 11-14. évfolyamról. A 11-12. évfolyamosok közül sokan informatika, digitális kultúra érettségi előkészítő fakultációra jelentkeztek, jártak, járnak és ebből érettségiznek/érettségiztek. A már korábban érettségizett 13-14. évfolyamosok körülbelül fele az OKJ utód szakmajegyzékhez tartozó szakképzésben tanult.

Mindegyik turnus azonos tematikával valósult meg. Turnusonként 3 db párhuzamos, 10-12 fős csoportokat indítottunk. Voltak közös elméleti programok, szakmai kirándulás, illetve külön-külön Java és Python nyelven megvalósuló gyakorlati programok, valamint projektbemutatóra is sor került. Igyekeztünk érinteni sokféle STEM területet: fizika, kémia, biológia, csillagászat, térinformatika, mesterséges intelligencia, szimuláció, játékprogramok, matematika, orvostudomány; mindegyiket a programozáshoz kapcsolva. Végeztünk tervezést, kódolást, tesztelést is. Belefért némi pályaorientáció is.

A STEM nyári tábor órarendje

Turnusonként 4 oktató kollégával és vendégelőadókkal hétfőtől-péntekig minden nap 8 és 18 óra között biztosítottuk a jelenlétet, felügyeletet. 40 órában szakmai programokat (elmélet+gyakorlat) kínáltunk. Reggelenként és késő délutánonként 1-1 órában offline, egyéni vagy csoportos játékok voltak kipróbálhatók. Ez mindösszesen 50 órát jelentett. Délelőttönként 20, 30 és 60 perces programokat terveztünk, délutánonként 120 és 240 perceseket. Szerdára szakmai kirándulást, gyárlátogatást ütemeztünk be. Íme az órarend áttekintő formában:

Íme az órarend naponként lapozható formában, benne a részletekkel:

Előzetes tapasztalataink

Előzetes tapasztalatainkat több forrásból merítettük, inspirálódtunk:

Köszönetnyilvánítás

Köszönjük résztvevő diákjainknak az aktivitást, a lelkesedést, a sok-sok elgondolkodtató kérdést, az offline kapott/szerzett élményeket, a pozitív visszajelzéseket.

Szeretnék köszönetet mondani együttműködő partnereinknek: LEGO Manufacturing Kft., REGIO Játékkereskedelmi Kft., Revolt Kereskedelmi Kft., Pannon Kincstár Humán Szakképző Központ.

Végül szeretnék köszönetet mondani minden oktató kollégámnak konstruktív részvételüként, kitartásukért a projekt teljes életciklusában. A tervezési, a szponzorszerző, a promóciós és a megvalósítási szakaszokban egyaránt 2023. április elejétől július végéig. Kiemelem korábbi és az aktuális projekthez kötődő tananyagfejlesztési tevékenységüket. A sikeresen lezárt projektünket augusztusban kipihenjük. 😉

Fibonacci-sorozat

Fibonacci logó

Fibonacci logóMa van (november 23.) a Fibonacci nap (újra). Fibonacci középkori matematikus volt, ő tette közismertté a Fibonacci-sorozat-ot. A (0), 1, 1, 2, 3, 5, 8, 13, 21, 34, sorozat igen népszerű azok közében is, akik programozást tanulnak. A sorozat első két eleme 1 és 1 (ha szükséges, akkor nulladik elemmel is dolgozhatunk), és minden további elem az előző két elem összege.

Korábban is blogoltak a kollégáim a témában:

Következzen most az én öt különböző megoldásom Java forráskódja, rövid magyarázattal. Mindegyik a Fibonacci-sorozat első tíz elemét állítja elő.

1. megoldás

Az első megoldás generikus listát épít. Az első két elemet elhelyezi a lista elején ( list.add(1)). Ezek a lista nulladik és első elemei lesznek. Ezután a metódus a maradék 8 elemmel 2-től n-1-ig fiktív indexként hivatkozva az előző két elem összegeként ( list.get(i-1)+list.get(i-2)) index nélkül bővíti a listát.

2. megoldás

A második megoldás a tipikusan nem hatékony rekurzív módszert implementálja. A rekurzív fib() függvény a sorozat egyetlen elemét adja vissza, amit (a függvényt) a ciklus sokszor meghív ahelyett, hogy a ciklus vagy a rekurzió „emlékezne” az előző elemekre.

3. megoldás

A harmadik megoldás funkcionális nyelvi elemeket (Stream API) használ. A folyamba kétdimenziós tömbre történő hivatkozással ( f-> new int[] ), közvetlen hozzárendeléssel/leképezéssel ( map()), kerülnek be a sorozat elemei.

4. megoldás

A negyedik megoldás a Fibonacci-számok zárt alakját használja. Másképpen ez a Binet-formula:

Ezzel a képlettel a sorozat elemei közvetlenül megadhatók, azaz nem szükséges más elemekre való hivatkozás. A ciklus adja meg, hogy a sorozat 1-10-ig indexelt elemei szükségesek.

5. megoldás

Az ötödik megoldás szintén Stream API-t használ. Először előállít egy sorozatot 1-10-ig, amiket a leképezésnél ( map()) inputként használ és alkalmazza rájuk a Binet-formulát. Hagyományos ciklus utasítás nem szükséges.

Eredmény

Mindegyik megoldás a konzolra írja szövegesen az eredményt, azaz a Fibonacci-sorozat első tíz elemét: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55. Érdemes elemezni a hatékonyság klasszikus három szempontja (időigény/lépésszám, tárigény, bonyolultság) alapján a különböző megoldásokat. Ezek mérésével könnyen kiegészíthetők a fenti metódusok, vagy az azokat meghívó osztályban a vezérlés.

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 9-12. óra: Metódusok, rekurzió és 17-28. óra Objektumorientált programozás alkalmaihoz kötődik.

Egy matematika érettségi feladat megoldása programozással 2022

érettségi logó

érettségi logóA 2022-es középszintű matematika érettségi feladatsor eléggé egyszerű volt, de azért a 6. feladata inspirált arra, hogy a programozás eszköztárával oldjuk meg ezt a feladatot. Szükséges hozzá a megszámolás programozási tétel. Többféle megoldás/megközelítés (iteratív és rekurzív) is előkerül. Érdekes belegondolni, hogy mennyire más lehetne a problémamegoldás, ha programozhatnánk a matematika érettségi vizsgán. A teljes feladatsor a megoldásokkal együtt letölthető az oktatas.hu-ról.

6. feladat

Egy feleletválasztós teszt 5 kérdésből áll, minden kérdésnél négy válaszlehetőség van. Hányféleképpen lehet az 5 kérdésből álló tesztet kitölteni, ha minden kérdésnél egy választ kell megjelölni?

1. megoldás

Rögtön tudjuk, hogy ez kombinatorika, n elem k-ad osztályú ismétléses variációja, amelynek paraméterei: n=4, k=5. A hatványozás azonosságainak ismeretében fejből is tudjuk a megoldást: 45=210=1024. A Java forráskód elvégzi a hatványozást. A Math.pow() függvény általánosabb, mint amire most szükségünk van. Fogad double valós paramétereket és double típusú értékkel tér vissza. Ezért hasznos az (int) explicit típuskényszerítés.

Másképpen: négy elemű halmazból öt elemet kiválasztunk és ezeket sorba rendezzük (permutáljuk) és egy elemet egy csoportban akár ötször is felhasználhatunk. Számít a sorrend. A lehetséges variációk száma: 1024.

2. megoldás

Ha hasznos lenne egy általános metódus az ismétléses variáció kiszámítására, akkor ez egy tipikus megoldás lehet erre. Kiegészítendő még a két paraméter előjelének ellenőrzésével.

3. megoldás

Ha a megértést segíti, akkor a teljes leszámolás (brute force) módszerével, egymásba ágyazott ciklusokkal könnyen kiírathatjuk a konzolra az 1024 db különböző válaszlehetőséget. A k-val kezdődő sorszámozott ciklusváltozók jelölik az öt kérdést, azon belül az 'a'-tól 'd'-ig karakterek adják a válaszlehetőségeket. Eredményül ezt kapjuk (görgethető):

4. megoldás

Ha csak a végeredmény szükséges, akkor ez az iteratív megoldás a megszámolás programozási tétellel előállítja azt.

5. megoldás

Ez egy rekurzív megoldás. Ciklus helyett a metódus önmagát hívja meg, így valósul meg az ismételt utasításvégrehajtás. A válaszlehetőségek összefűzésével (konkatenáció) előállított válasz akkor megfelelő, ha annak hossza öt. Ez esetben kiíródik a válaszlehetőség a konzolra (mintegy mellékhatásként). Ugyanazt az eredményt kapjuk, mint a 3. megoldásnál.

6. megoldás

Szintén, ha csak a végeredmény szükséges, akkor ez a mellékhatással rendelkező rekurzív metódus előállítja azt. A mellékhatás most az, hogy a metódus eljárás és nem függvény és szükséges hozzá a db osztályváltozó (ami a metódushoz képest globálisnak is tekinthető).

7. megoldás

Ez a megoldás a válaszlehetőségeket megfelelteti n alapú számrendszerben k számjegyből álló számoknak. A kétdimenziós tömbben számokat tárol, így:

  • 1,…,1,1 → 0…0000
  • 1,…,1,2 → 0…0001
  • 1,…,1,n → 0…001(n1)
  • 1,…,2,n → 0…001(n1)
  • n,…,n,n → (n1)...(n1)

Végül a kiíró ciklus ezeket a számokat karakterekké alakítja ( 'a' ASCII kódja 97) és fordított sorrendben írja ki, hogy ugyanazt az eredményt kapjuk, mint a 3. megoldásnál.

Továbbfejlesztési lehetőségek

  • A 2. megoldáshoz: teszteljük le a lehetséges túlcsordulást és az int típus helyett szükség esetén használjunk long típust!
  • A 3. megoldáshoz: építsünk kétdimenziós tömb adatszerkezetet, amiből később az i-edik válaszlehetőség megadható!
  • Előzőhöz: állítsuk elő lexikografikus sorrendben az i-edik válaszlehetőséget adatszerkezet felépítése nélkül!
  • A 6. megoldáshoz: valósítsuk meg a rekurzív gondolatmenetet mellékhatás nélkül!
  • Teszteljünk: mennyi idő alatt hajtódik végre a 4. és a 6. megoldás? Mekkora paraméterekkel érzékelhető, hogy a rekurzió jóval lassabban fut?
  • A 7. megoldáshoz: cseréljük le az egésztömb adatszerkezetet karaktertömbre!

A bejegyzéshez tartozó teljes forráskódot ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Ajánljuk matematika érettségi feladat címkénket, mert a témában évről-évre blogolunk.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek, valamint 21-24. óra: Objektumorientált programozás 1. rész alkalmaihoz kötődik.