érettségi logó

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

érettségi logóA 2020-as emelt szintű matematika érettségi feladatsor 9. feladata inspirált arra, hogy a programozás eszköztárával oldjuk meg ezt a feladatot. Szükséges hozzá kollekció adatszerkezet és néhány programozási tétel. É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.

2018-ban és 2019-ben is kiválasztottam egy-egy matematika érettségi feladatot a középszintű feladatlapról és megoldottam Java nyelven. 2020-ban az emelt szintű feladatsornál lelkesedtem eléggé, hogy blogoljak róla.

9. feladat

Egy városban a közösségi közlekedést kizárólag vonaljeggyel lehet igénybe venni, minden utazáshoz egy vonaljegyet kell váltani. A vonaljegy ára jelenleg 300 tallér. Az utazások száma naponta átlagosan 100 ezer. Ismert az is, hogy ennek kb. 10%-ában nem váltanak jegyet (bliccelnek).
A városi közlekedési társaság vezetői hatástanulmányt készíttettek a vonaljegy árának esetleges megváltoztatásáról. A vonaljegy árát 5 talléronként lehet emelni vagy csökkenteni. A hatástanulmány szerint a vonaljegy árának 5 talléros emelése várhatóan 1000-rel csökkenti a napi utazások számát, és 1 százalékponttal növeli a jegy nélküli utazások (bliccelések) arányát. (Tehát például 310 talléros jegyár esetén naponta 98000 utazás lenne, és ennek 12%-a lenne bliccelés.) Ugyanez fordítva is igaz: a vonaljegy árának minden 5 talléros csökkentése 1000-rel növelné a napi utazások számát, és 1 százalékponttal csökkentené a bliccelések arányát. A tanulmány az alkalmazott modellben csak a 245 tallérnál drágább, de 455 tallérnál olcsóbb lehetséges jegyárakat vizsgálta.

  • a) Mekkora lenne a közlekedési társaság vonaljegyekből származó napi bevétele a hatástanulmány becslései alapján, ha 350 tallérra emelnék a vonaljegyek árát?
  • b) Hány talléros vonaljegy esetén lenne maximális a napi bevétel?

Tervezés

Értelmezve a feladatot és a feltett kérdéseket: adódik, hogy a megoldáshoz szükséges egy POJO, ami az összetartozó adatokat fogja egybe objektumként. Mivel több kell belőle, célszerű egy indexelhető adatszerkezet, például tömb vagy lista. Ékezettelen magyar elnevezéseket fogok használni. A POJO osztály neve legyen Kozlekedes és a beszédes nevű tulajdonságai legyenek a következők: vonaljegyAr, napiUtasszam, bliccelesSzazalek, napiBevetel. Mindegyik nemnegatív egész szám és belefér az int primitív típus számábrázolási tartományába.

Ha a konstruktor paraméterként átveszi az input vonaljegyAr-at, akkor abból a többi adatot egyszerű képletekkel előállíthatja. Hasznos, ha a konstruktor ellenőrzést is végez. A tanulmány az alkalmazott modellben limitálja a vonaljegy árát (250 és 450 közötti öttel osztható számként). Az öttel oszthatóság az emelés/árváltozás mértékéből adódik. Ha a vonaljegy ára nem megfelelő, akkor a konstruktor kivételt dob, amivel megakadályozza, hogy az alkalmazott modellhez nem illeszkedő tulajdonságokkal rendelkező objektum létrejöjjön.

Az output meghatározásához az a) és b) feladatban megfogalmazott kérdésekből kell kiindulni. Ezekből adódik, hogy szükséges két getter metódus a POJO-ba:  getVonaljegyAr() és getNapiBevetel(). Persze könnyen generáltatható az összes getter is, de setter nem kell. Ezeken kívül a tesztelés megkönnyítésére hasznos egy toString() metódus is, amellyel a 4 összetartozó adat hozzáférhető és megjeleníthető a konzolon.

A belépési pont és egyben a vezérlés egy másik osztályban valósul meg. Itt feltöltjük a tanulmány alkalmazott modelljének megfelelően előállított objektumokkal (memóriacímeikkel) a generikus listát, amit programozási tételekkel (kiválasztás, szélsőérték-kiválasztás) dolgozunk fel.

A POJO osztály forráskódja

A vezérlő osztály forráskódja

A main() metódus feltölti a generikus lista adatszerkezetet az alkalmazott modellben lehetséges/előforduló vonaljegyAr alapján létrehozott objektumokkal (a memóriacímükkel). A feladat9Megoldas1() metódus paraméterként átveszi a feldolgozandó listát.

Az a) feladatra a választ kiválasztás programozási tétellel kapjuk meg. A kérdés így szól: melyik az (első) olyan objektum, amelyben a vonaljegyAr egyenlő 350-nel? A ciklust követően megkapjuk, hogy az i-edik az, amelyikre igaz a feltétel. (Az nem merül fel, hogy van-e ilyen objektum, hiszen tudjuk, hogy van. Csak az a kérdés, hogy melyik az. Több sem lehet.) A  lista.get(i).getNapiBevetel() művelettel elkérjük az i-edik objektumtól a válaszadáshoz szükséges napi bevételt.

A b) feladatra a választ szélsőérték-kiválasztás programozási tétellel kapjuk meg. A kérdés így szól: melyik az (első) olyan objektum, amelyben a napiBevetel a maximális? (Mivel a lista nem üres, így létezik a legnagyobb napi bevétel. Mivel nem biztos, hogy a legnagyobb napi bevétel egyedi, ezért merül fel az első a kérdésben.) Tegyük fel, hogy a nulladik objektumra igaz a feltétel: azaz maxIndex=0. Később a ciklusban változtassuk meg a maxIndex-et, ha a feldolgozás során találunk nagyobb értéket. Szélsőérték-kiválasztásnál a kezdeti elemet nem hasonlítjuk össze saját magával (hiszen úgysem különbözne), ezért indul a for ciklus 1-ről. A ciklust követően a  lista.get(maxIndex).getVonaljegyAr() művelettel elkérhetjük a maxIndex-edik objektumtól a válaszadáshoz szükséges vonaljegy árát.

A program által felépített adatszerkezet

Ha a vezérlőben aktiváljuk a megjegyzésben szereplő kiíratást, akkor a konzolon megjelennek a main() metódusban létrehozott listában lévő objektumok adatai (amilyen viselkedést a POJO toString()-jébe programoztunk. A 246 soros szöveg görgetéssel megtekinthető.

Az eredmény

A program konzolon/szövegesen jeleníti meg a válaszokat a feltett két kérdésre:

Gondoljuk újra

Az első megoldás 41 elemű listát épít. Persze ez a lista több mindenre is jó lehet, ha több(féle) kérdést kell(ene) megválaszolni. Ezért tekinthetjük strukturális tartaléknak.

A két konkrét kérdésre azonban úgy is adhatunk választ, hogy nem építünk lista adatszerkezetet. Ez a második megoldás. A feladat9Megoldas2() metódusnak nincs paramétere és azonos eredmény ad.

Az a) feladat: egy névtelen objektumként létrehozott POJO-tól azonnal elkérhetjük a választ, ami mehet rögtön a konzolra. Ez a kiválasztás programozási tétel extrém/legjobb esete, hiszen az első objektum jó is lesz, ciklust sem kell szervezni.

A b) feladat: kiindulunk a legolcsóbb vonaljegyből és tegyük fel, hogy ekkor a legnagyobb a napi bevétel. Ciklussal léptessük a vonaljegy árát ötösével legfeljebb a legdrágábbig. Léptetés közben mindig csak azt a dinamikusan létrehozott objektumot „jegyezzük meg”, amelyiktől a röptében elkért napi bevétel a korábbihoz – az addig legnagyobbnak vélthez – képest nagyobb. Végül a megmaradó POJO-tól elkérhető a maximális napi bevételhez tartozó vonaljegy ára. Ez a szélsőérték-kiválasztás programozási tétel megvalósítása dinamikusan: kezdetben nem áll rendelkezésre az összes adat, ami alapján döntést kell hozni, ehelyett az adatokat menet/feldolgozás közben állítjuk elő és „eldobjuk” azt, ami már nem kell.

Nekem ezek a programozással való megoldások sokkal jobban tetszenek, mint az oktatas.hu-n elérhető hivatalos, matematikai megoldás, amihez differenciálszámítás is kell. Persze aki emelt szinten érettségizik matematikából, annak az sem jelenthet gondot és biztosan izgalmasnak találja.

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, 9-12. óra: Metódusok, rekurzió, valamint 17-24. óra: Objektumorientált programozás alkalmaihoz kötődik.

Szólj hozzá!