Népesedési világnap

Népesedési világnap logó

Népesedési világnap logóAz ENSZ 1987-ben július 11-ét a népesedési világnappá (World Population Day) nyilvánította. Bolygónk lakossága aznap érte el az 5 milliárdot. További kerek számok voltak: 1999. október 12-én 6 milliárd, 2011. október 30-án 7 milliárd. További kerek számok várhatóak: 2023 – 8 milliárd, 2037 – 9 milliárd, 2057 – 10 milliárd. A KSH elemzése részletes elemzéseket közöl évről-évre a témában, például: 2019-ben, 2018-ban. A worldometer.info weboldalon folyamatosan frissülő kimutatások érhetők el a népességhez globálisan, valamint országonként is: például Magyarország aktuális népesedési adatai.

A népesedési világnap inspirált egy Java program megtervezésére és megírására. A swing GUI-s program megjeleníti a worldometer.info weboldalról kinyerhető adatok alapján régiónként (kontinensenként) az elérhető adatokat 1950-től 2020-ig az alábbiak szerint egy világtérképen.

Az elkészült program

Népesedési világnap Java program

Tervezés

Objektumorientált szemlélettel, MVC architekturális tervezési mintát követünk, angol nyelvű interfész, osztály, változó, objektum, metódus nevekkel. A projekt neve: WorldPopulation, a csomag neve: worldpopulation. Amit lehet, konstansként interfészbe (szeparálva) teszünk és az MVC rétegekhez kötődő osztályok implementálják. A modell minden évszámhoz tárolja a szükséges adatokat, mindezt egyetlen betöltéssel/letöltéssel éri el. A program kliensként hat régióra vonatkozó adatot gyűjt össze, alkalmazkodva a szerver adatforráshoz. A címsorban lévő összesített adat is elérhető közvetlenül a weboldalon, de a kisebb adatforgalom érdekében hasznos inkább a kliensben összesíteni. Mindössze egyetlen eseménykezelés szükséges: a csúszka beállításával megadott évszám alapján frissíteni kell a régiók címkéit és az ablak címsorát. Öröklődés hasznos a feladat megoldása során: egyrészt interfészek, másrészt osztályok között.

Interfészek

Az ősinterfész a WorldPopulationConstants, benne az évszám intervallum MIN_YEAR és MAX_YEAR határaival, valamint a megjeleníthető régiók neveivel tömbben: REGION_NAME_ARRAY. Két utódinterfész épül az ősre: ModelConstants és ViewConstants. Előbbi interfész az adatforráshoz kapcsolódik: URL_COMMON az URL eleje, URL_ARRAY az URL végei régiónként tömbben. Utóbbi interfész a megjelenítéshez kapcsolódik: WORLD_MAP_IMAGE a háttérkép annak WORLD_MAP_RECT méretével együtt, valamint a régiónkénti REGION_RECT_ARRAY téglalapok tömbje a kezdeti pozíciókkal/méretekkel, TITLE a sablon a program címsorához (frissítendő az évszámmal és az összesített népességgel). A megfelelő utódinterfészt mindig implementálja az MVC szerint hozzá illeszkedő osztály.

Osztályok

A belépési pont a WorldPopulation.java fájlban található.

Három összetartozó elemi adatot fog össze egybe a RegionData POJO, ezek name, year, population nevű rendre String, int, long típusú adatok. Például: Európa, 2020, 747643253. Tartalmaz két függvényt: getPopulation(), valamint toString(). Utóbbi HTML formátumban adja vissza a megjelenítendő adatokat.

A JLabel-ből származik az igényekhez alakított RegionLabel osztály. Ennek van előre megadott pozíciója, mérete, betűtípusa, betűmérete, sárga háttérszíne, piros kerete. Ezenkívül a téglalap átlátszó, valamint a benne megjelenő HTML tartalom vízszintesen középre igazított. Némi extra funkció, hogy egérrel megfogva – drag and drop – áthelyezhető, ami a MouseMotionListener egérmozgást figyelő interfész mouseDragged() metódusának felülírásával válik lehetővé. A mozgathatóságáért saját maga felel. Példaként közöljük az osztály teljes forráskódját:

A webről adatokat szerez és tárolja a Model osztály, a java.io és java.net csomagokra építve. Egy példa: a https://www.worldometers.info/world-population/europe-population/ oldal forrásából nyeri ki az osztály az alábbi adatokat:

Ezek parszolását követően elkészül egy optimálisnak tekinthető, generikus listákból álló regionListArray tömb adatszerkezet. A parszolás történhet egyszerű szövegkezeléssel vagy JSON feldolgozással is. Erre épülnek a konstruktorral és vezérlővel összehangoltan működő getter metódusok: getHTML(), getRegionList(), getRegionData(), getPopulation(). A JSON adatforrás feldolgozását most nem részletezzük, de hasonlóról blogoltunk már: Időjárás Budapesten.

A grafikus felhasználói felületet adja a JFrame utód View osztály. Három GUI komponensből áll: pnWorldMap – háttérkép JPanel, lbYear – kiválasztott/aktuális év JLabel, slYear – kiválasztható/görgethető aktuális év JSlider. Izgalmas megoldani egymásra/egymáson elhelyezni a komponenseket. Egy JLayeredPane komponens  DEFAULT_LAYER rétegére kerül a térképet tartalmazó háttérkép, majd a  PALETTE_LAYER rétegére kerül dinamikusan a hat  RegionLabel osztályú/típusú objektum. A csúszka komponens slYearStateChanged() eseménykezelő metódusa vezérlőként megszólítja a modell réteget és a visszakapott adatokkal frissíti a nézet réteget (a címsorban lévő összesítéssel együtt, ezres szeparátorokkal).

Ötlet továbbfejlesztésre

Hat különböző weboldal forráskódjából kell összegyűjteni a megjelenítendő adatokat. Ez 2020-ban régiónként 71 számot jelent és hat régió van. Érdemes lehet olyan adattárolást megvalósítani, amely csökkenti a szerverhez fordulások számát, illetve a letöltendő adatok mennyiségét. Hiszen a múltbeli évekhez kötődő historikus adatok nem változnak. Ha ezekre valamilyen formában a program emlékszik, akkor elegendő az utolsó tárolt évből kiindulva az aktuális évig évenként, régiónként lekérni mindössze 6, 12, 18… számot, a program utolsó futtatásának évéből kiindulva. Ez lényegesen kevesebb lenne, mint a jelenlegi 6*71 lekért szám. A koncepció kulcsszava: inkrementális adatfrissítés. Ha megvalósítjuk az ötletet, akkor figyelni kell arra, hogy az aktuális/utolsó évben az adatok akár másodpercenként is változhatnak.

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 tematikájához kötődik (ha a swing GUI-ra koncentrálunk és az adatok helyi fájlrendszerből elérhetők), és a Java EE szoftverfejlesztő tanfolyam tematikájához kapcsolódik (ha az adatokat közvetlenül a webről olvassuk).

Multimédia az oktatásban 2020

NJSZT-MMO logó

NJSZT-MMO logóA Neumann János Számítógép-tudományi Társaság (NJSZT) „Multimédia az oktatásban” Szakosztály által – évente – szervezett XXVI. Multimédia az oktatásban című online nemzetközi konferencia került megrendezésre 2020. június 11-12-én. Az online platformot a Pexip webkonferencia szolgáltatás biztosította.

A konferencia célja

A szakmai rendezvény célja, hogy elősegítse az oktatás, valamint a kutatás és fejlesztés különböző területein dolgozó, oktató hazai és külföldi szakemberek, PhD és felsőoktatási hallgatók kapcsolatfelvételét, tapasztalatok és jó gyakorlatok cseréjét, egyes képzési szakterületekhez kapcsolódó kreditek gyűjtését.

24 témakörben hirdették meg az előadóknak a jelentkezési lehetőséget, köztük néhány hozzánk kötődő

  • élethelyzethez igazított tanulás,
  • a multimédia alkalmazása a felsőoktatásban és a felnőttképzésben,
  • mLearning, eLearning és környezete,
  • a tanulási környezet technikai, technológiai változása,
  • felhőalapú szolgáltatások,
  • multimédia és a tudományos kutatás összefonódása,
  • multimédia-fejlesztések, eredmények, alkalmazások bemutatása.

Letölthető a konferencia absztrakt kötete, benne a konferencia programjával.

Részt vettem a konferencián

Már többször is részt vettem előadóként ezen a konferencián szakmai előadással, magyar és/vagy angol nyelvű cikkel, poszterrel az oktatói csapat tagjaival együtt. Ezek a publikációs listámban megtalálhatók. Legutóbb tavaly, lásd Multimédia az oktatásban 2019.

2020-ban előadást tartottam „Analysis of the Sorting with distribution and counting algorithm; is it worth or not?” (Szétosztva leszámoló rendezési algoritmus hatékonyságának vizsgálata, jobb vagy sem?) címmel 20 percben, amely a konferencia „Multimédia-fejlesztések, eredmények, alkalmazások bemutatása/ Az űrkutatás és űrtevékenység a modern oktatásban” című szekciójába került. Előadásom rövid, angol nyelvű összefoglalója az absztrakt kötet 19-20. oldalán olvasható. Előadásom prezentációját ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

Előadásom témája a Java SE szoftverfejlesztő tanfolyam 5-8. óra: Vezérlési szerkezetek, 9-12. óra: Metódusok, rekurzió, 13-16. óra: Tömbök alkalmaihoz kapcsolódik.

Multimédia az oktatásban online nemzetközi konferencia 2020

A teljes konferencia élő közvetítésben zajlott a Dunaújvárosi Egyetemről. 2 nap alatt 51 előadás hangzott el. 5 országból 300 fő követte. Egy-egy szekciót átlagosan 50 fő látogatott. A plenáris előadások a mesterséges intelligencia témakörhöz kapcsolódtak. Külön kiemelném Gulyás István kiváló összefoglalóját a 4 ipari forradalomról. A program szerteágazó, színvonalas és izgalmas volt. Az aktuális digitális oktatás technológiai és módszertani kihívásaival több előadás is foglalkozott. Számomra hasznosnak bizonyult az ezekhez kötődő többféle nézőpontból való megközelítés. A szakmai társalgás virtuális konferenciaszobákban folyt, és a szervezők még további 3 beszélgetős sarkot is létrehoztak a kötetlen kommunikáció biztosítására. A szervezés abszolút profi volt, a korábbi évek offline eseményeihez hasonlóan. Jövőre is szívesen csatlakozom a rendezvényhez. 2021-ben a Multimédia az oktatásban konferencia helyszíne a Dunaújvárosi Egyetem lesz.

Céline Dion – Courage World Tour

Céline Dion Courage World Tour

Céline Dion Courage World TourA Céline Dion – Courage World Tour esettanulmányunkban a turné első részének koncerthelyszíneit jelenítjük meg Google Charts segítségével. Ebben a blog bejegyzésben a tervezés, megvalósítás lépéseit tekintjük át és megmutatjuk az eredményeket. A Java és JavaScript forráskódokat most nem részletezzük.

Háromféle grafikont használunk

  • idővonal (Timeline) időpontok és helyszínek Gantt diagram-szerűen,
  • térkép (Geo Chart) városok megjelölésével és időpontok jelmagyarázatban,
  • tematikus térkép az USA államaival (szintén Geo Chart), az állam területén adott koncertek száma alapján és db jelmagyarázatban.

A tervezés és megvalósítás lépései

  1. Adatokat kell szerezni egy weboldal (https://www.celinedion.com/in-concert/) feldolgozásával ( saveHTML()). Ehhez a művelet a GET. Figyelni kell a megfelelő User-Agent paraméterezésére és a karakterkódolásra ( ISO-8859-1). A kapott bemeneti folyam feldolgozását pufferelt módon érdemes elvégezni. Célszerű az adatforgalom minimalizásása érdekében a weboldal tartalmát helyi fájlba menteni ( tour.html). Ügyelni kell a kötelező és az ajánlott kivételkezelésre.
  2. Értelmezni kell a tour.html fájlt. A HTML tartalom végén JSON formátumban beágyazva elérhetők a koncert turné állomásainak adatai: nekünk kell a város ( city), helyszín ( venue), dátum/idő ( startDate). Érdemes külön fájlba menteni a tour.html-ből a JSON tartalmat ( tour.json), mert abból egyszerűen betölthető ( saveJSON()).
  3. Tanulmányozni kell a Google Charts diagramok közül azt a kettőt, amiket testre kell szabni: Timeline és Geo Chart. Tudni kell: mi a diagramot tartalmazó weboldal állandónak tekinthető eleje és vége (ezeket hasznos külön interfészben konstansként tárolni: HTMLFileContent), valamint mi az adatoktól függő része (közepe). Ismerni kell a szükséges metaadatok és adatok formátumát. Érdemes átnézni a különböző testre szabási és formázási lehetőségeket a fenti diagramtípusoknál (esetleg a többi típusból is meríthetünk ötleteket).
  4. A koncert turné állomásainak összetartozó 3 adatát le kell képezni POJO-vá ( Event). Ezt érdemes privát változókkal ( city, venue, startDate) és factory metódussal megvalósítani. Célszerű, ha az adatok visszakérésére alkalmas getter metódusok is készülnek ( getTimelineChartDataTableRow(), getGeoChartDataTableRow()), amelyek kiszolgálják a megfelelő diagramtípus igényeit.
  5. A tour.json fájl feldolgozásával (parszolásával) Event típusú generikus listába vagy JSON tömbbe könnyen leképezhetők az adatok.
  6. Hasznos egy vezérlőosztály létrehozása, amely a 3 diagramtípust előállító (HTML fájlt generáló) metódust ( createTimelineChart(), createGeoChartCity(), createGeoChartCountry()) valamint a belépési pontot ( main()) tartalmazza.
  7. Generálható az idővonalat tartalmazó timelineChart.html fájl a createTimelineChart()metódussal. Ehhez 5 oszlop megadása szükséges (ebben a sorrendben): label, city, tooltip, start, end. Az első 3 adat string, az utolsó 2 adat date típusú. Egy példa Event: ['2019.09.18.', 'Québec, QC', 'Videotron Centre', new Date(2019, 09, 18, 19, 0, 0), new Date(2019, 09, 18, 21, 0, 0)].
  8. Regisztrálni kell egy Google Cloud Platform felhasználói fiókot és tanulmányozni kell a geokódolás folyamatát és lehetőségeit, hiszen a városok nevéből (formátum pl.: 'Minneapolis, MN') szükség lesz azok térképi koordinátáira. Aktiválni kell a szolgáltatás használatához szükséges mapsApiKey-t.
  9. Generálható a városokat tartalmazó geoChartCity.html fájl a createGeoChartCity() metódussal. Ehhez 3 oszlop megadása szükséges (ebben a sorrendben): city, dateCity, no . Egy példa Event: ['Minneapolis, MN', '2019.11.01. Minneapolis, MN', 1].
  10. Generálható a régiókat/államokat tartalmazó geoChartCountry.html fájl a createGeoChartCountry() metódussal. Ez egy tematikus térkép: a különböző színek jelölik az egy régió/állam városaiban tartott koncertek számát. Ehhez az adatok megfelelő rendezését követően végrehajtott csoportváltás algoritmus szükséges. 2 oszlop megadása szükséges: country, concertNo. Egy példa adatsor: ['US-TX', 3].

Az eredmények

TimelineChart grafikon:

GeoChartCity grafikon:

GeoChartCountry grafikon:

Érdemes megismerni további – térképekhez kapcsolódó – grafikontípusokat is: Geomap, Intensity Map.

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 példák a Java SE szoftverfejlesztő tanfolyam 37-44. óra: Fájlkezelés és a Java EE szoftverfejlesztő tanfolyam 1-4. óra: Elosztott alkalmazások, webszolgáltatások és 13-16. óra: JSON feldolgozás alkalmaihoz kötődnek.

Top 5 fizetésű alkalmazottak listája

Top5 fizetés

Top5 fizetésAz a fela­da­tunk, hogy az Oracle HR sé­má­ból le­kér­dez­ve állít­suk elő a top 5 fizetésű alkalmazottak listáját, a fizetések csökkenő sorrendjében. Ez egytáblás lekérdezéssel megvalósítható. Az EMPLOYEES táb­lában megtalálható az összefűzött névhez szükséges FIRST_NAME és LAST_NAME mezők, valamint a fizetés a SALARY mezőben. Min­den alkalmazottnak van neve és fizetése. Előfordul legalább 5 különböző fizetés.

Oracle HR séma

Tanfolyamainkon többféleképpen modellezzük és tervezzük meg a feladat megoldását.

Megoldás (Java SE szoftverfejlesztő tanfolyam)

A Java SE szoftverfejlesztő tanfolyam 45-52. óra: Adatbázis-kezelés JDBC alapon alkalmain a következők szerint modellezünk és tervezünk.

Kiindulunk az alábbi egyszerű lekérdező parancsból (V1):

Top5SalaryV1Select

Eredményül ezt kapjuk (részlet, V1):

Top5SalaryV1 Eredmény

A kapott 107 rekordból álló eredménytáblát a Java kliensprogram fejlesztése során leképezzük egy generikus POJO listába, a rekordonként összetartozó két adatból előállítva az objektumok tulajdonságait. Kiderül, hogy a 17000 többször is előfordul. Mivel bármely fizetés előfordulhatna többször is, így előre nem tudjuk, hogy az eredménytáblából mennyi rekordot kell áttölteni a listába. A fizetésekből generikus halmazt építhetve, addig tudjuk folytatni a beolvasást, amíg a halmaz elemszáma kisebb ötnél. Eredményül hat rekordot kapunk. A Java kliensprogram forráskódját most nem részletezzük, de tanfolyamaink hallgatói számára ILIAS e-learning tananyagban tesszük elérhetővé a teljes forráskódot. Ennél a megoldásnál egyszerűbb a lekérdező parancs, de több feladat hárul a Java kliensprogramra.

Lássunk néhány tévutat és az általános megoldás helyett konkrét megoldásokat! Ha szeretnénk adatbázis oldalon megoldani a feladatot, akkor használhatnánk a ROWNUM pszeudooszlopot. Ez 1-től sorszámozza az eredménytáblát, így használható lehetne arra, ha limitálni szeretnénk a visszaadandó rekordok számát.

1. elvi hibás lekérdező parancs:

Top5SalaryV2 Select

1. elvi hibás eredmény:

Top5SalaryV2 Eredmény

A hiba elvi, a lekérdező parancs szintaktikailag helyes. A harmadik oszlopban látjuk, hogy a rekordok sorszámozása megtörténik, de a kapott nevek és fizetések eltérnek a V1 esetben kapott helyes eredménytől. Az okokat természetesen megbeszéljük. Támpont: próbáljuk meg a lekérdező parancs feltételében kicserélni az 5-öt például 10-re és próbáljuk megmagyarázni, miért kapjuk azt, amit kapunk. Továbbá a konkrét esetben tudjuk, hogy hat rekordot kellene kapunk. Felmerülhet a gyanú, hogy a rendezés túl későn történik meg. Megpróbáljuk zárójelezéssel és lekérdezések egymásba ágyazásával befolyásolni a WHERE és ORDER BY alparancsok végrehajtási sorrendjét.

2. elvi hibás lekérdező parancs:

Top5SalaryV3 Select

2. elvi hibás eredmény:

Top5SalaryV3 Eredmény

A hiba most is elvi, a lekérdező parancs szintaktikailag helyes. A zárójelezés valóban hatással van a két alparancs végrehajtási sorrendjére és megfigyelhető, hogy a harmadik oszlopban a rekordok táblabeli fizikai sorrendje jelenik meg és a feltétel ( ROWNUM <= 5) nem a mező értékére, hanem a rekordok darabszámára értendő. Nyilván az 5-öt 6-ra módosítva visszakaphatnánk a V1 első hat rekordját, de ez nem lenne általános megoldás. Más úton is eljuthatunk a konkrét megoldáshoz.

3. elvi hibás lekérdező parancs:

Top5SalaryV4 Select

3. konkrét megközelítéssel kapott helyesnek látszó eredmény:

Top5SalaryV4 Eredmény

A hiba most is elvi, a lekérdező parancs szintaktikailag helyes. Általános megoldás helyett konkrét megoldásként megkapjuk a V1 első hat rekordját, de ehhez be kellett építeni a lekérdező parancsba a 13000-et. Ez a Top 5-ben legkisebb fizetés. Megbeszéljük, hogy miért hasznos a DISTINCT módosító/kulcsszó beépítése a lekérdező parancsba.

Megoldás (Java adatbázis-kezelő tanfolyam)

A Java adatbázis-kezelő tanfolyam 9-12. óra: Oracle HR séma elemzése, 13-16. óra: Konzolos kliensalkalmazás fejlesztése JDBC alapon, 1. rész, 33-36. óra: Grafikus kliensalkalmazás fejlesztése JDBC alapon, 2. rész alkalmával a következők szerint modellezünk és tervezünk.

Most arra helyezzük a hangsúlyt, hogy back-end, azaz adatbázis oldalon állítsuk elő az eredményt és ezáltal a front-end, azaz a Java kliensprogram egyszerűbb lehet. A lekérdező parancsot belülről kifelé haladva gondoljuk végig. Először kell egy halmaz a különböző fizetésekről csökkenő sorrendben. Utána ebből kell az első öt darab, amelyek halmazt alkotnak. Végül erre építve kell azoknak az alkalmazottaknak a neve és fizetése, akiknek a fizetése benne van a halmazban.

1. majdnem helyes megoldás:

Top5SalaryV5 Select

1. általános megközelítéssel kapott helyesnek látszó eredmény:

Top5SalaryV4 Eredmény

A probléma az, hogy az adatok helyes sorrendje a véletlennek köszönhető. Ha a lekérdező parancs feltételében az 5 helyett nagyobb számokat helyettesítünk be, akkor ez jól megfigyelhető. A következő megoldás már ezt a problémát is kezeli.

Finomítva a 3. elvi hibás lekérdező parancsot, a konkrét 13000 helyettesíthető belső lekérdező paranccsal. Építsük ezt be az 1. helyes megoldásba úgy, hogy az IN predikátum helyett használjuk a nagyobb vagy egyenlő hasonlító operátort. A középső lekérdező parancs a halmaz helyett már csak egyetlen értéket adjon vissza, amelyhez könnyű hasonlítani az aktuális alkalmazott fizetését. Ezzel kiváltható a nagyobb memóriaigényű halmazban való tartalmazottságot eldöntő művelet, a jóval hatékonyabb egy értékkel való összehasonlítással. Memóriaigény szempontjából nem maga a konkrét művelet/operátor az érdekes, hanem a használatukhoz szükséges adatok előállítása, mennyisége, tárolása, feldolgozása.

2. helyes megoldás:

Top5SalaryV6 Select

2. általános megközelítéssel kapott helyes eredmény:

Top5SalaryV4 Eredmény

Közben az is kiderült, hogy miért szükséges két helyen az ORDER BY alparancs.

Végül, ha ismerjük az Oracle DENSE_RANK() analitikai függvényét, amely egy rendezett lista különböző elemeihez rendel sorrendben számokat (másképpen rangsort állít fel 1-től kezdve), akkor elkészíthetjük az alábbi megoldást.

3. helyes megoldás:

Top5SalaryV7 Select

3. általános megközelítéssel kapott helyes eredmény:

Top5SalaryV7 Eredmény

Érdemes átgondolni és összehasonlítani a többféle különböző megközelítés lehetőségeit, korlátait. Ha egyensúlyozni kell a kliensprogram és az adatbázis-szerver terhelése között, valamint az MVC modell összetettsége, karbantarthatósága, könnyen dokumentálhatósága a/is szempont, akkor többféle alternatív módszer is bevethető, valamint építhetünk a különböző Oracle verziók (dialektusok) képességeire is.

Az SQL forráskódok formázásához a Free Online SQL Formatter-t használtam.

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

érettségi logó

é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.