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

érettségi logó

érettségi logóA 2017-es középszintű matematika érettségi feladatsor 12. feladata inspirált egy Java program megírására. Szükséges hozzá néhány programozási tétel: sorozatszámítás, megszámolás, valamint adatszerkezetként ideális egy kétdimenziós tömb. É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.

12. feladat

Egy kockával kétszer egymás után dobunk. Adja meg annak a valószínűségét, hogy a két dobott szám összege 7 lesz! Válaszát indokolja!

Matematikai megoldás

A feladat nagyon egyszerű. Két megoldást ismertet a javítási-értékelési útmutató:

  • Összesen 6 * 6 = 36-féleképpen dobhatunk. Hat olyan dobáspár van, amelyben 7 az összeg: (1; 6), (2; 5), (3; 4), (4; 3), (5; 2) és (6; 1). A keresett valószínűség 6/36-od, vagyis egyhatod.
  • Bármennyit is dobunk elsőre, ezt a második dobás egyféleképpen egészítheti ki 7-re. Így a második dobásnál a hat lehetséges értékből egy lesz számunkra kedvező. A keresett valószínűség egyhatod.

Közelítő megoldások szimulációval

Egy alkalom két kockadobást jelent egymás után. A dobások sorrendje nem számít (alkalmanként és összességében sem). Minél több alkalommal végezzük el a kockadobásokat, annál jobban megközelítjük a fenti valószínűséget (várható értéket, bővebben: nagy számok törvénye). Az egyhatod közelítő értéke a Java double adattípusával 0.16666666666666666.

1. megoldás

Ha nem akarunk emlékezni a dobásokra, összegükre, csupán megszámolnánk, hogy hány olyan dobáspár van, amelyben 7 az összeg, akkor ehhez mindössze egy számláló ciklus kell, aminek a ciklusmagjában két véletlen kockadobás összegét előállítjuk és növelünk egy számlálót/gyűjtőt, ha az éppen 7. Az eredményt a számláló és a ciklus lépésszámának hányadosa adja meg. Például meghívhatjuk a metódust így: kockadobas1(5000); és kaphatjuk eredményül ezt: 5000 alkalomból 7 összegként 836 alkalommal fordult elő. Valószínűség: 0.1672 . A metódus kivételt dob, ha értelmetlen a paramétere. Íme a metódus Java forráskódja:

2. megoldás

Ha egy 13 elemű egész típusú tömböt használhatunk emlékezetként. Kezdetben 2-től 12-ig indexelve nullázzuk ki, így csoportos gyűjtést tudunk megvalósítani. A nullázás most inicializáló blokkal történt, mert nem sok eleme van a tömbnek (sok elemnél inkább használjunk erre ciklust). A tömb első két elemét nem használjuk semmire. Mi történik a ciklusban? Például dobas1=3 és dobas2=4 esetén a dobasDbTomb[7] elemét növeli (mindegy mi volt ott korábban, de inkrementálódjon). Most több adatot tárolunk, mint amiből megválaszolható a feladatban megfogalmazott konkrét kérdés, de ezt tekinthetjük strukturális tartaléknak.

Hasonló, egydimenziós tömbbel történő belső adattárolást megvalósító elosztott alkalmazásról blogoltunk már: Kockadobás kliens-szerver alkalmazás.

3. megoldás

Ez az igazi szimuláció, swing GUI grafikus környezetben, ahogyan az alábbi képernyőképen látható. A megvalósítás kétdimenziós tömböt használ adatszerkezetként. Álljon 7 sorból és 7 oszlopból és legyen i a sor- és j az oszlopindex. A tömb [0][0]-dik elemét nem használjuk semmire. Az első oszlopába ( j=0 és i>0) bekerülhetnek a dobókockán előforduló számok 1-től 6-ig. Hasonlóan az első sorba ( i=0 és j>0) is. Ezek a dobott számok alapján indexek lesznek és az ábrán zöld hátterű cellákba kerültek. A tömb többi eleme kezdetben 0 (nulla), ezek az ábrán fehér hátterű cellák. A szürke hátterű cellák (mellékátló) esetén a dobott számok összege 7 és jól látszik, hogy ez hatféleképpen fordulhat elő a 36-féle eset közül. Például a 2. sor 5. oszlopában lévő szám mutatja, hogy a 10000 alkalomból 274-szer fordult elő az, hogy a dobáspár a (2; 5) lett. A tömb két indexe felcserélhető lenne, mert ez a mellékátlóban lévő számok összegét nem befolyásolja.

Kockadobás program képernyőkép

A programban kiválasztható néhány alkalomból amit szeretnénk, és a Dob nyomógombra kattintva indul el időzítővel a folyamat. Várakoztatás/menet közben piros színnel kiemelve látszik/megfigyelhető, hogy az éppen aktuális dobás hol növeli az értéket/előfordulást/darabszámot. A képernyőképen befejeződött állapot látható. Az eredményt a szürke cellákban lévő számok összegének és az alkalmak számának hányadosa adja meg. Ezt a háttérbeli kétdimenziós tömbben összesítéssel az alábbi Java forráskód-részlet adja meg:

Most lényegesen több adatot tárolunk, mint ami a konkrét válaszhoz kell, de cserébe jól érzékeltethető a csoportos gyűjtés/megszámolás működése. A program grafikus felhasználói felületének felépítését és az eseménykezelés megvalósítását most nem részletezzük.

Eszünkbe juthatna, hogy a program miért dob kétszer 1 és 6 közötti számot egymás után és ezt összegzi, amikor egyetlen 2 és 12 közötti dobással (véletlenszám generálással) megkaphatnánk a dobáspár összegét. Hiszen két db 1 és 6 közötti szám összege mindig 2 és 12 közötti szám. Jó lenne ez az ötlet/megvalósítás? Igen? Nem? Miért? A hozzászólásokhoz várjuk az indoklást.

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

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 alkalmaira épülő 29-36. óra: Grafikus felhasználói felület alkalmaihoz kötődik.

Kígyókocka grafikus felületen

Kígyókocka

KígyókockaA JavaFX grafikus felhasználói felületének és eseménykezelésének megvalósítása némileg eltér más Java GUI implementációk működésétől, például swing vagy Java3D. Főként animációk során hasznos használni. Megközelítése természetesen objektumorientált: a térbeli objektumok koordinátái, viselkedésük, transzformációkkal valósul meg, és azok is objektumok. A korábban elkészített konzolos kígyókocka programot valósítjuk meg most JavaFX GUI-val.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.

A program működése

Kígyókocka JavaFX grafikus felületen

A program megvalósítása

A start() JavaFX életciklust indító eljárás felépíti a createGridUI() függvényt meghívva felhasználói felületet (színpad/jelenet JavaFX-ben), beállítva a méreteket, címsort, és meghívja az eseménykezelésért felelős handleRotateButtons() eljárást.

A createGridUI() függvény a grafikus felhasználói felület elemeit paraméterezi (szerepe szerint Factory metódus). Öt elemből álló rács ( GridPane osztályú grid nevű objektum) készül el, amelyre nyilakat tartalmazó nyomógombok (pl.: Button típusú btLeft objektum) kerülnek fel a négy égtájnak megfelelően, valamint rajta középen helyezkedik el a kígyókocka 3D megjelenítését megvalósító objektum. A nyilak entitásai az Unikód karaktertáblából származnak. A kígyókocka objektumot a meghívott createSnakeCube() függvény hozza létre. A Node osztályú snakeCube nevű objektum geometriai transzformációs objektumot is hozzá kell rendelni: ez most a négyirányú forgatást megvalósítani képes névtelen Rotate osztályú objektum lesz. A forgatást 5 paraméterrel célszerű beállítani (van rá megfelelő túlterhelt konstruktor), ezek rendre: szög, X, Y, Z tengely origója és a forgatás tengelye. Az objektumok tulajdonosi hierarchiája swing-es szemmel nézve szokatlannak tűnik, de szemléletben legalább azonos a Java3D és a JavaFX megvalósítás.

A createSnakeCube() függvény előállítja a színpadra/jelenetbe kikerülő kígyókockát Node osztályú objektumként. A konstans CUBE tömb egységvektor rendszerben tartalmazza a kígyót alkotó kockák középpontjait. Az első ciklus mindezt nagyítást alkalmazva skálázza. A második ciklus koordináta és pont transzformációk alkalmazásával ( moveToMidPoint: eltolás középre, rotateAroundCenter: forgatás a középpont körül) a kiinduló állapotnak megfelelő méretben és pozícióban elhelyezi a kígyó útvonalát mutató hengerobjektumokat. A konstrukciós és transzformációs műveletek esetén alkalmazkodni kell ahhoz, hogy a JavaFX koordinátarendszerben az X jobbra, az Y lefelé, a Z pedig befelé (a nézőponttól távolodva a térben) növekszik. A matematikai hátteret részletesen most nem magyarázzuk el.

A handleRotateButtons() eljárás a forgatás 4 nyíl eseménykezelésének hozzárendelést végzi el. A nyomógomb objektumok setOnAction() hozzárendelő metódusának paramétere EventHandler funkcionális interfésszel és lambda művelettel működik. A forgatás irányát hozzárendeljük a megfelelő nyomógombhoz. Ez még csak végrendelkezés a jövőre: csak definiáljuk, hogy minek kell majd történnie, ha bekövetkezik az esemény (valamelyik nyílra/nyomógombra kattint a felhasználó).

A rotateSnake() eljárás minden nyíl feliratú nyomógombra kattintva reagál a bekövetkezett eseményre. A rotateAxis objektum a forgatás tengelye, a paraméterként átvett direction enum-tól függ, szinkronban azzal a nyomógombbal, amelyikre kattintott a felhasználó.

Ötletek a továbbfejlesztésre

  • Lehetne többféle irány is, például a négy sarokba átlós vagy mélységi irányú elforgatással.
  • Beépülhetne többféle transzformáció is, például skálázás (kicsinyítés, nagyítás), eltolás (közelítés, távolítás).
  • A kígyó útvonalát mutató hengerobjektumok kirajzolásának sorrendjén lehetne változtatni, mert a megjelenítés nem tökéletes. Jelenleg néhány helyzetben lehetetlennek, Escher lehetetlen konstrukcióihoz hasonlónak tűnhet a kígyókocka. Ha a tér mélységéből a nézőpont felé közeledve rajzolnánk ki a hengerobjektumokat, akkor a 3D látvány nem sérülne.

Tanfolyamainkon JavaFX grafikus felülettel hangsúlyosan nem foglalkozunk, de egy-egy kész forráskódot közösen megbeszélünk, és össze is hasonlítjuk a swing-es változattal. Fejlesztünk játékprogramot, de inkább konzolosan, vagy swing-es ablakban, vagy webes alkalmazásként.

A grafikus felületek felépítésének megismerése fontos lépcső az objektumorientált programozás elmélyítéséhez, gyakorlásához. A grafikus felületekhez egy másik lényeges szemléletváltás is kapcsolódik, hiszen a korábbi algoritmusvezérelt megközelítést felváltja az eseményvezérelt (eseménykezelés).

Tudatosan hangsúlyozott MVC-s projektben megoldva a feladatot, a modell rétegben tárolhatnánk többféle kígyókocka megjelenítéséhez és animációjához szükséges adatszerkezetet és transzformációs objektumokat/metódusokat is és a nézet/vezérlő rétegekben biztosíthatnánk ezek közül a kijelölést/kiválasztást menüvel, ikonokkal, eszköztárral, gyorsbillentyűkkel.

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

Tanfolyamaink orientáló moduljának 9-12. óra: Mesterséges intelligencia alkalmához kapcsolódóan a kígyókocka véletlenszerű előállítása helyett stratégiával rendelkező visszalépéses algoritmust specifikálhatunk és implementálhatunk.

Ez egy két részből álló blog bejegyzés 2. része. A blog bejegyzés 1. része itt található.

CHOO + CHOO = TRAIN

Most nem a híres kisvonatról van szó, hanem egy ismert kriptoaritmetikai feladványról. Ebben a feladattípusban egyszerű matematikai műveletek szerepelnek és a különböző betűk különböző számjegyeket jelölnek. Általában többféleképpen megoldhatók: intuíció, ötlet, módszeres próbálkozás, következtetés, kizárás vagy klasszikus behelyettesítés. Ha van megoldás és meg is találunk egyet, akkor a következő kérdés az, hogy van-e még, illetve összesen hány megoldás van?

Íme a feladat:

Érdemes minden megoldás során figyelembe venni a minden számjegyet 0-9-ig végigpróbáló lépések helyett legalább az alábbi öt feltételt:

  • C >= 5, hiszen CHOO olyan négyjegyű szám, aminek a kétszerese ötjegyű szám,
  • T = 1, mivel két négyjegyű szám összege 10000 < TRAIN < 20000 (ebben az esetben),
  • O >= 6, hiszen maradéknak képződnie kell, mert I és N különbözik,
  • 2 <= N < I és
  • I >= 3 és szintén a maradékképződés miatt.

Esetleg még tovább gondolkodva, felfedezhetünk egyéb összefüggéseket, illetve kizárhatunk egyéb értékeket, így jelentősen csökkenthető egy-egy Java implementáció lépésszáma.

1. megoldás

Ez adatszerkezet nélküli megoldás, így eléggé összetett feltétellel valósul meg a művelet teljesülése (megfelelő helyiértékek használatával) és a különbözőségek vizsgálata.

2. megoldás

Itt az ellenőrzési feltétel egyszerűbb, mert a különbözőség/egyediség tesztelését áthárítottam a halmazszerűen működő HashSet generikus kollekcióra, építve annak beépített képességére.

Mit gondolsz, melyik megoldás hajtódik végre gyorsabban? Miért?

Mivel a két megoldásnál a ciklusok szervezése megegyezik, így a használt adatszerkezet dönt (hiszen annak konstrukciós és szelekciós, azaz karbantartási műveletei vannak). Az 1. megoldás a gyorsabb, mert nem használ adatszerkezetet. A 2. megoldás lényegesen lassabban fut, mert a generikus kollekció műveletei miatt az automatikus szemétgyűjtő tevékenység erősen igénybe vett. A különbség nagyságrendileg 15-szörös.

A feladatnak két megoldása van: 5466 + 5466 = 10932 és 5488 + 5488 = 10976.

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

Akinek kedve támadt, lásson hozzá hasonló feladatokhoz:

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.