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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class Kozlekedes { private int vonaljegyAr; //pl.: 300 tallér private int napiUtasszam; //pl.: 100000 fő private int bliccelesSzazalek; //pl.: 10% private int napiBevetel; //pl.: 27000000 tallér public Kozlekedes(int vonaljegyAr) { if(vonaljegyAr<=245 || vonaljegyAr>=455 || vonaljegyAr%5!=0) throw new IllegalArgumentException( "A vonaljegy ára nem megfelelő. (Hibakód: 32596)"); this.vonaljegyAr=vonaljegyAr; napiUtasszam=100000-(vonaljegyAr-300)/5*1000; bliccelesSzazalek=10+(vonaljegyAr-300)/5; napiBevetel=(vonaljegyAr* (int)(napiUtasszam*(100-bliccelesSzazalek)/100.0)); } public int getVonaljegyAr() { return vonaljegyAr; } public int getNapiBevetel() { return napiBevetel; } @Override public String toString() { return "Közlekedés {\n"+ " vonaljegy ár: "+vonaljegyAr+" tallér\n"+ " napi utasszám: "+napiUtasszam+" fő\n"+ " bliccelés: "+bliccelesSzazalek+"%\n"+ " napi bevétel: "+napiBevetel+" tallér\n"+ "}"; } } |
A vezérlő osztály forráskódja
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public class MatErettsegi2020EmeltFeladat9 { private static void feladat9(List<Kozlekedes> lista) { //a) feladat int i=0; while(lista.get(i).getVonaljegyAr()!=350) i++; System.out.println( "a) Mekkora lenne a közlekedési társaság vonaljegyekből\n"+ "származó napi bevétele a hatástanulmány becslései alapján,\n"+ "ha 350 tallérra emelnék a vonaljegyek árát?\n"+ lista.get(i).getNapiBevetel()+" tallér lenne a napi bevétel.\n"); //b) feladat int maxIndex=0; for(i=1; i<lista.size(); i++) if(lista.get(i).getNapiBevetel()> lista.get(maxIndex).getNapiBevetel()) maxIndex=i; System.out.println( "b) Hány talléros vonaljegy esetén lenne maximális "+ "a napi bevétel?\nHa a vonaljegy ára "+ lista.get(maxIndex).getVonaljegyAr()+" tallér lenne.\n"); } public static void main(String[] args) { List<Kozlekedes> lista=new ArrayList<>(); for(int vonaljegyAr=250; vonaljegyAr<=450; vonaljegyAr+=5) lista.add(new Kozlekedes(vonaljegyAr)); // for (Kozlekedes kozlekedes : lista) // System.out.println(kozlekedes); // System.out.println(); feladat9Megoldas1(lista); // feladat9Megoldas2(); } } |
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ő.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
Közlekedés { vonaljegy ár: 250 tallér napi utasszám: 110000 fő bliccelés: 0% napi bevétel: 27500000 tallér } Közlekedés { vonaljegy ár: 255 tallér napi utasszám: 109000 fő bliccelés: 1% napi bevétel: 27517050 tallér } Közlekedés { vonaljegy ár: 260 tallér napi utasszám: 108000 fő bliccelés: 2% napi bevétel: 27518400 tallér } Közlekedés { vonaljegy ár: 265 tallér napi utasszám: 107000 fő bliccelés: 3% napi bevétel: 27504350 tallér } Közlekedés { vonaljegy ár: 270 tallér napi utasszám: 106000 fő bliccelés: 4% napi bevétel: 27475200 tallér } Közlekedés { vonaljegy ár: 275 tallér napi utasszám: 105000 fő bliccelés: 5% napi bevétel: 27431250 tallér } Közlekedés { vonaljegy ár: 280 tallér napi utasszám: 104000 fő bliccelés: 6% napi bevétel: 27372800 tallér } Közlekedés { vonaljegy ár: 285 tallér napi utasszám: 103000 fő bliccelés: 7% napi bevétel: 27300150 tallér } Közlekedés { vonaljegy ár: 290 tallér napi utasszám: 102000 fő bliccelés: 8% napi bevétel: 27213600 tallér } Közlekedés { vonaljegy ár: 295 tallér napi utasszám: 101000 fő bliccelés: 9% napi bevétel: 27113450 tallér } Közlekedés { vonaljegy ár: 300 tallér napi utasszám: 100000 fő bliccelés: 10% napi bevétel: 27000000 tallér } Közlekedés { vonaljegy ár: 305 tallér napi utasszám: 99000 fő bliccelés: 11% napi bevétel: 26873550 tallér } Közlekedés { vonaljegy ár: 310 tallér napi utasszám: 98000 fő bliccelés: 12% napi bevétel: 26734400 tallér } Közlekedés { vonaljegy ár: 315 tallér napi utasszám: 97000 fő bliccelés: 13% napi bevétel: 26582850 tallér } Közlekedés { vonaljegy ár: 320 tallér napi utasszám: 96000 fő bliccelés: 14% napi bevétel: 26419200 tallér } Közlekedés { vonaljegy ár: 325 tallér napi utasszám: 95000 fő bliccelés: 15% napi bevétel: 26243750 tallér } Közlekedés { vonaljegy ár: 330 tallér napi utasszám: 94000 fő bliccelés: 16% napi bevétel: 26056800 tallér } Közlekedés { vonaljegy ár: 335 tallér napi utasszám: 93000 fő bliccelés: 17% napi bevétel: 25858650 tallér } Közlekedés { vonaljegy ár: 340 tallér napi utasszám: 92000 fő bliccelés: 18% napi bevétel: 25649600 tallér } Közlekedés { vonaljegy ár: 345 tallér napi utasszám: 91000 fő bliccelés: 19% napi bevétel: 25429950 tallér } Közlekedés { vonaljegy ár: 350 tallér napi utasszám: 90000 fő bliccelés: 20% napi bevétel: 25200000 tallér } Közlekedés { vonaljegy ár: 355 tallér napi utasszám: 89000 fő bliccelés: 21% napi bevétel: 24960050 tallér } Közlekedés { vonaljegy ár: 360 tallér napi utasszám: 88000 fő bliccelés: 22% napi bevétel: 24710400 tallér } Közlekedés { vonaljegy ár: 365 tallér napi utasszám: 87000 fő bliccelés: 23% napi bevétel: 24451350 tallér } Közlekedés { vonaljegy ár: 370 tallér napi utasszám: 86000 fő bliccelés: 24% napi bevétel: 24183200 tallér } Közlekedés { vonaljegy ár: 375 tallér napi utasszám: 85000 fő bliccelés: 25% napi bevétel: 23906250 tallér } Közlekedés { vonaljegy ár: 380 tallér napi utasszám: 84000 fő bliccelés: 26% napi bevétel: 23620800 tallér } Közlekedés { vonaljegy ár: 385 tallér napi utasszám: 83000 fő bliccelés: 27% napi bevétel: 23327150 tallér } Közlekedés { vonaljegy ár: 390 tallér napi utasszám: 82000 fő bliccelés: 28% napi bevétel: 23025600 tallér } Közlekedés { vonaljegy ár: 395 tallér napi utasszám: 81000 fő bliccelés: 29% napi bevétel: 22716450 tallér } Közlekedés { vonaljegy ár: 400 tallér napi utasszám: 80000 fő bliccelés: 30% napi bevétel: 22400000 tallér } Közlekedés { vonaljegy ár: 405 tallér napi utasszám: 79000 fő bliccelés: 31% napi bevétel: 22076550 tallér } Közlekedés { vonaljegy ár: 410 tallér napi utasszám: 78000 fő bliccelés: 32% napi bevétel: 21746400 tallér } Közlekedés { vonaljegy ár: 415 tallér napi utasszám: 77000 fő bliccelés: 33% napi bevétel: 21409850 tallér } Közlekedés { vonaljegy ár: 420 tallér napi utasszám: 76000 fő bliccelés: 34% napi bevétel: 21067200 tallér } Közlekedés { vonaljegy ár: 425 tallér napi utasszám: 75000 fő bliccelés: 35% napi bevétel: 20718750 tallér } Közlekedés { vonaljegy ár: 430 tallér napi utasszám: 74000 fő bliccelés: 36% napi bevétel: 20364800 tallér } Közlekedés { vonaljegy ár: 435 tallér napi utasszám: 73000 fő bliccelés: 37% napi bevétel: 20005650 tallér } Közlekedés { vonaljegy ár: 440 tallér napi utasszám: 72000 fő bliccelés: 38% napi bevétel: 19641600 tallér } Közlekedés { vonaljegy ár: 445 tallér napi utasszám: 71000 fő bliccelés: 39% napi bevétel: 19272950 tallér } Közlekedés { vonaljegy ár: 450 tallér napi utasszám: 70000 fő bliccelés: 40% napi bevétel: 18900000 tallér } |
Az eredmény
A program konzolon/szövegesen jeleníti meg a válaszokat a feltett két kérdésre:
1 2 3 4 5 6 7 |
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? 25200000 tallér lenne a napi bevétel. b) Hány talléros vonaljegy esetén lenne maximális a napi bevétel? Ha a vonaljegy ára 260 tallér lenne. |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private static void feladat9Megoldas2() { //a) feladat System.out.println( "a) Mekkora lenne a közlekedési társaság vonaljegyekből\n"+ "származó napi bevétele a hatástanulmány becslései alapján,\n"+ "ha 350 tallérra emelnék a vonaljegyek árát?\n"+ new Kozlekedes(350).getNapiBevetel()+ " tallér lenne a napi bevétel.\n"); //b) feladat Kozlekedes kMax=new Kozlekedes(250); for(int vonaljegyAr=255; vonaljegyAr<=450; vonaljegyAr+=5) { Kozlekedes k=new Kozlekedes(vonaljegyAr); if(k.getNapiBevetel()>kMax.getNapiBevetel()) kMax=k; } System.out.println( "b) Hány talléros vonaljegy esetén lenne maximális "+ "a napi bevétel?\nHa a vonaljegy ára "+ kMax.getVonaljegyAr()+" tallér lenne.\n"); } |
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.
Gyakorlásképpen megírtam rekurzióval a POJO lista bejárását. Megmutatom majd a mai óránkon.
Rendben Tamás.
Közben vettük a kivételeket is, ezért a POJO konstruktorát kiegészíteném a
throws IllegalArgumentException
-nel. Kihatással van ez a forráskód más részeire?Tamás: attól függ, hogy milyen típusú a kivétel ebből a szempontból: kötelezően ellenőrzött (checked) vagy sem (unchecked). Az
IllegalArgumentException
az utóbbi, így ez nem hat a konstruktor hívása körüli forráskódra. A fájlkezelésnél majd megnézzük az előbbi típust is, ott már más lesz a helyzet. Rövid áttekintés: https://www.geeksforgeeks.org/checked-vs-unchecked-exceptions-in-java/