A 2021-es középszintű matematika érettségi feladatsor 12. feladata inspirált arra, hogy a programozás eszköztárával oldjuk meg ezt a feladatot. Szükséges hozzá néhány programozási tétel: sorozatszámítás, eldöntés, megszámolás, kiválogatás. Többféle megoldás/megközelítés 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.
12. feladat
A háromjegyű pozitív egész számok közül véletlenszerűen kiválasztunk egyet. Mennyi annak a valószínűsége, hogy a kiválasztott szám számjegyei különbözők? Megoldását részletezze!
1. megoldás
1 2 3 4 5 6 7 8 9 |
static void feladat12Megoldas1() { int db=0; for(int szazas=1; szazas<=9; szazas++) for(int tizes=0; tizes<=9; tizes++) for(int egyes=0; egyes<=9; egyes++) if(szazas!=tizes && tizes!=egyes && szazas!=egyes) db++; System.out.println("1. megoldás: "+db/900.0); } |
Az 1. megoldás egymásba ágyazott ciklusokkal behelyettesíti a szóba jöhető 900 db háromjegyű szám számjegyeit. A feltétel 648 esetben teljesül. Három számjegy azonosságát két részfeltétel és kapcsolatával eldönthetnénk a trichotómia miatt. Három számjegy különbözőségéhez három részfeltétel és kapcsolatából áll össze a feltétel. A válasz a kedvező és összes eset aránya/hányadosa, azaz 0,72. Másképpen 648 db szám a 900 db háromjegyű szám közül. A megoldás lépésszáma 900.
2. megoldás
1 2 3 4 5 6 7 8 9 10 |
static void feladat12Megoldas2() { int db=0; for(int szazas=1; szazas<=9; szazas++) for(int tizes=0; tizes<=9; tizes++) if(szazas!=tizes) for(int egyes=0; egyes<=9; egyes++) if(tizes!=egyes && szazas!=egyes) db++; System.out.println("2. megoldás: "+db/900.0); } |
Az egymásba ágyazott ciklusok lépésszáma összeszorzódik. A legbelső ciklus az előtte lévő feltételtől függően kevesebbszer is végrehajtódhat, hiszen a százas és tízes helyiértéken lévő számjegyek egyezése esetén nincs értelme az egyes helyiértéken lévő számjegy vizsgálatának. Így a 2. megoldás lépésszáma 810, azaz 10%-kal kevesebb. Ez a három részből álló feltétel két részre bontásával érhető el.
3. megoldás
1 2 3 4 5 6 7 8 9 10 11 |
static void feladat12Megoldas3() { int db=0; for(int i=100; i<=999; i++) { int szazas=i/100; int tizes=i%100/10; int egyes=i%100%10; if(szazas!=tizes && tizes!=egyes && szazas!=egyes) db++; } System.out.println("3. megoldás: "+db/900.0); } |
A 3. megoldásban egyetlen ciklus végzi a vizsgálatot, a megszámolást. A ciklusváltozó már nem számjegy, hanem maga a háromjegyű szám, amiről döntést kell hozni: különbözik-e mindegyik számjegye vagy sem. Három beszédes nevű segédváltozó segít értelmezni a Java forráskódot. Ezek az egész osztás és a maradékos osztás műveleteivel állíthatók elő.
4. megoldás
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static boolean kulonbozoSzamjegyek(int i) { int szazas=i/100; int tizes=i%100/10; if(szazas==tizes) return false; int egyes=i%100%10; if(tizes==egyes || szazas==egyes) return false; return true; } static void feladat12Megoldas4() { int db=0; for(int i=100; i<=999; i++) if(kulonbozoSzamjegyek(i)) db++; System.out.println("4. megoldás: "+db/900.0); } |
A 4. megoldás logikai visszatérési értékű segédfüggvényt alkalmaz. Ez egy menekülőutas megoldás. Ha kizáró feltétel szerint már döntést tudunk hozni (például megegyezik a százas és a tízes helyiértéken lévő számjegy), akkor hamis értékkel menekülünk. Egyébként ág nélkül ezután következhet az egyes helyiértéken lévő számjegy összehasonlítása a többivel. A második feltétel az eddigiekhez képest tagadott, mert a menekülés a cél. Ha nincs menekülés amiatt, hogy volt két megegyező számjegy, akkor – a feltételek egymásra épülése miatt – nincs más hátra, mint igaz értékkel visszatérni (ami azt jelenti, hogy nem volt egyezés, azaz minden számjegy különbözött).
5. megoldás
1 2 3 4 5 6 7 8 9 10 11 |
static long kulonbozoSzamjegyDb(int i) { return IntStream.of(i/100, i%100/10, i%100%10).distinct().count(); } static void feladat12Megoldas5() { int db=0; for(int i=100; i<=999; i++) if(kulonbozoSzamjegyDb(i)==3) db++; System.out.println("5. megoldás: "+db/900.0); } |
Az 5. megoldás segédfüggvénye a háromjegyű szám esetén a különböző számjegyek darabszámával tér vissza. A röptében előállított százaz, tízes, egyes helyiértékeken lévő számjegyekből folyam adatszerkezet készül, aminek feldolgozását a Stream API műveletei (egyediesítő, megszámoló) végzik el. Ezt a vezérlő ciklusban hárommal összehasonlítva léptethető a megszámolást megvalósító változó, hiszen ha teljesül a feltétel, akkor eggyel több megfelelő szám van, mint előtte volt.
6. megoldás
1 2 3 4 5 6 7 8 9 10 11 |
static void feladat12Megoldas6() { int db=0; for(int i=100; i<=999; i++) { int szazas=i/100; int tizes=i%100/10; int egyes=i%100%10; if(szazas==tizes || tizes==egyes || szazas==egyes) db++; } System.out.println("6. megoldás: "+(900-db)/900.0); } |
Az 6. megoldás újra másképpen közelít. Ha könnyebbnek tűnik az a feltétel, hogy mikor nem jó (kedvezőtlen) nekünk egy szám, akkor beépíthetjük ezt is. Megszámoljuk azokat a háromjegyű számokat, amelyeknél egy vagy két számjegy azonos, majd ez kivonjuk a háromjegyű számok darabszámából.
7. megoldás
1 2 3 4 5 6 |
static void feladat12Megoldas7() { long db=IntStream.range(100, 1000). filter(i -> IntStream.of(i/100, i%100/10, i%100%10). distinct().count()==3).count(); System.out.println("7. megoldás: "+db/900.0); } |
A 7. megoldás már mindent folyamokkal old meg, azok képességeire építve. Az összes háromjegyű számot előállítja, majd rajtuk kiválogatás programozási tételt (szűrőt) használ (az 5. megoldás segédfüggvényére építve), végül a folyamban maradó számokat megszámolja. Ez a megoldás már olyan haladóknak való, akik magabiztosan építik össze a Stream API műveleteit és a lambda kifejezéseket. Mindent egyben. Persze hol itt a hatékonyság? Hozzászólásokban megbeszélhetjük.
8. megoldás
1 2 3 4 5 6 7 8 9 |
static void feladat12Megoldas8() { long db=IntStream.range(100, 1000). filter(i -> !(i/100==i%100/10 && i%100/10==i%100%10)). //1. szűrő filter(i -> !(i/100==i%100/10)). //2. szűrő filter(i -> !(i%100/10==i%100%10)). //3. szűrő filter(i -> !(i/100==i%100%10)). //4. szűrő count(); System.out.println("8. megoldás: "+db/900.0); } |
A 8. megoldás szintén folyam adatszerkezettel működik, de négy egymást követő lépésben végez szűrést (kiválogatást). A 900 db háromjegyű számból indulunk ki. Az 1. szűrő kihagyja a 9 db AAA számot, amelyek számjegyei azonosak és így marad utána 891 db szám. A 2. szűrő után marad 810 db szám, mert kimarad az a 81 db AAB alakú szám (ahol a százas és tízes helyiértéken lévő számjegyek megegyeznek) az összesen 90 db-ból, ami még a folyamban maradt az 1. szűrő után. A 3. szűrő kihagy 81 db ABB alakú számot és meghagy 729 db számot. A 4. szűrő kihagy 80 db ABA alakú számot és meghagy 648 db ABC alakú számot.
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, 13-16. óra: Tömbök, valamint 21-24. óra: Objektumorientált programozás, 2. és 3. rész alkalmaihoz kötődik.
Gondolkodtam a 7. megoldáson. Általánosítottam úgy, hogy bármennyi számjegyből állhat egy szám és könnyen szétszedhető számjegyekre a
String.valueOf(i).chars().map(Character::getNumericValue)
utasítással és ez mehet a filterbe.Örülök András, hogy már itt tartasz. Ha jól emlékszem Rád a legutóbbi Programozási Hét 2020 rendezvényünkről, akkor 16 éves vagy. Elgondolkodtatókat kérdeztél már akkor is. Ez az ötlet is jó, ahogyan a megvalósítás is.
Helyes a kiinduló adat, de közben már történt egy ++ az életkoromnál.