Sokan szerencsés vagy balszerencsés napnak tartják a péntek tizenharmadikát. Évente 1-2-3 alkalommal megtörténik, hogy a hónap 13. napja péntekre esik (minden vasárnap kezdődő hónapban). A hónap 13. napja valamivel valószínűbben péntekre esik, mint a hét bármely más napja. Átlagosan 212,35 naponként fordul elő péntek 13. Előfordulhat két egymást követő hónapban is, de akár 14 hónap is eltelhet két péntek 13 között.
A nap említése sok helyen előfordult: regényekben, filmekben, híres emberek születése vagy halála is esett péntek 13-ra. Átlag alatti közlekedési baleset szokott előfordulni ezeken a napokon – talán mert az emberek óvatosabbak. Kimutatható összefüggést/korrelációt, „péntek 13 hatást” figyeltek meg a tőzsdén is.
Hasznos lehet, ha írunk egy Java programot, amely néhány egymást követő év esetén listázza a konzolra azokat a hónapokat, amikor 13-a péntekre esik.
Tervezés
Legyen egy listFriday13(year) eljárás, amely a paraméterként átvett évben kiírja azokat a hónapokat a konzolra, amelyekben 13-a péntekre esett/esik. Például: 2017: január, október. A hónapok nevei magyar nyelven jelenjenek meg. Az adott év hónapjain végighaladó ciklus legyen hatékony. Optimalizáljunk a ciklus lépésszámára! A ciklus álljon le, ha már talált 3 hónapot (mivel nem lehet több).
1. megoldás
A megoldást a tematika Tömbök témakörében az alábbiak szerint készíthetjük el. Előismeretek: változók, operátorok, ciklusok, programozási tételek, metódusok, tömbök, String összehasonlítás. Az ismert öröknaptár algoritmusokból implementáljuk az egyiket, például:
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 |
static boolean isLeapYear(int year) { return (year>=1582) && ((year%4==0 && year%100!=0) || (year%400==0)); } public static String dayOfWeek(int year, int month, int day) { int centuryCode=2*(3-year/100%4); int yearOfCentury=year%100; int monthCode=new int[] {-1, isLeapYear(year)?6:0, isLeapYear(year)?2:3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }[month]; int dayCode=(centuryCode+yearOfCentury+ yearOfCentury/4+monthCode+day)%7; return new String[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }[dayCode]; } public static void listFriday13v1(int year) { String[] months={"", "január", "február", "március", "április", "május", "június", "július", "augusztus", "szeptember", "október", "november", "december"}; System.out.print(year+": "); for(int month=1, count=1; month<=12 && count<=3; month++) if(dayOfWeek(year, month, 13).equals("Friday")) { System.out.print(months[month]+", "); count++; } System.out.println(); } |
A listFriday13v1(year) eljárásban az elemi döntés egyszerű: dayOfWeek(year, month, 13).equals("Friday"). Épít az öröknaptárt megvalósító – saját – szöveget visszaadó függvényre. A függvény az algoritmus szerinti kódok előállításához ( centuryCode, monthCode, dayCode) felhasználja a szökőév ( isLeapYear(year)) függvényt, valamint két – konstansnak is tekinthető – névtelen tömböt ( new int[], new String[]).
2. megoldás
A megoldást a tematika Objektumorientált programozás témakörében az alábbiak szerint készíthetjük el. Felhasználjuk eddigi ismereteinket és a JDK beépített dátumkezelő (tároló, formázó) funkcióit (osztályok, interfészek, konstansok, felsorolások).
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static void listFriday13v2(int year) { SimpleDateFormat dfMonth= new SimpleDateFormat("MMMM", new Locale("hu")); ArrayList<String> listMonths=new ArrayList<>(); for(int month=1, count=1; month<=12 && count<=3; month++) { GregorianCalendar date=new GregorianCalendar(year, month-1, 13); if(date.get(Calendar.DAY_OF_WEEK)==Calendar.FRIDAY) { listMonths.add(dfMonth.format(date.getTime())); count++; } } System.out.println(year+": "+String.join(", ", listMonths)); } |
A listFriday13v2(year) eljárás a Calendar absztrakt osztály konstansait használja fel az elemi döntéshez: date.get(Calendar.DAY_OF_WEEK)==Calendar.FRIDAY. A dátumot a GregorianCalendar konstruktora példányosítja és figyelni kell a 0-bázisú hónapkezelésre. A dátum formázása során ( dfMonth) beállítjuk a megfelelően paraméterezett ( "hu") Locale típusú objektumot és a hónap hosszú nevét kérjük ( "MMMM"). A metódus generikus listába gyűjti a kiválasztott hónapok nevét, amiket végül a String.join() függvény fűz össze a megjelenítéshez.
Eredmény
A vezérlésben egy ciklus 2017-től 2036-ig szervezve az alábbi eredményt adja:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Péntek 13 előfordulások 2017: január, október 2018: április, július 2019: szeptember, december 2020: március, november 2021: augusztus 2022: május 2023: január, október 2024: szeptember, december 2025: június 2026: február, március, november 2027: augusztus 2028: október 2029: április, július 2030: szeptember, december 2031: június 2032: február, augusztus 2033: május 2034: január, október 2035: április, július 2036: június |
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 a fentiek szerint: 13-16. óra: Tömbök alkalom, illetve 17-28. óra: Objektumorientált programozás alkalom.
Aki gyakorolna a témához kötődően: írjon olyan Java programot, ami listázza a konzolra a 21. század éveit olyan hónapokba csoportosítva, amikor 13-a péntekre esik. Egy év többször is előfordulhat. Például: január – 2006, 2012, 2017, 2023, 2034, 2040, 2045, 2051, 2062, 2068, 2073, 2079, 2090, 2096. A megoldásokat hallgatóinktól az ILIAS-ra, érdeklődőinktől hozzászólásban várjuk.
Ehhez a feladathoz találtam egy JavaScript-es megoldást. Hasonló a Java-s megoldáshoz, viszont itt meg lehet adni a kezdő évet, és a záró évet, ezenkívül meg lehet adni, hogy csak a kiválasztott hónapot jelenítse meg a program. A formátum amit vissza ad a program angol formátum, először mutatja a hónapot, aztán a napot, utoljára pedig az évet. Érdemes kipróbálni: http://tmashour.tripod.com/javascript/friday_13th.html
Nagyon tetszik a programnak a felépítése. Én is találtam egy hasonlót, ami hasonlít ehhez: https://stackoverflow.com/questions/13335175/javacreating-a-program-to-calcuate-friday-the-13ths-given-a-year-month-date-a
Ádám, Barnabás: köszi mindkettőtöknek.
Akad néhány péntek 13-hoz kötődő szerencsétlen történelmi dátum:
https://en.wikipedia.org/wiki/Triskaidekaphobia
/Ha már újra 🙂 péntek 13 van./
Nohát, hogy mik vannak! Köszönjük Anikó.
Gyakorlásként a
listFriday13v1()
-et lambda műveletekkel megoldottam. Kristóf: megnéznéd a megoldásomat az ILIAS-on?Igen Noel, és a mai órán meg is beszélhetjük.
Hamarosan újra lesz péntek 13. Ez adta az ötletet. Összekombinálva a két megoldást, megoldottam a gyakorló feladatot. Íme a forráskód:
Az eredmény (172 előfordulás):
A tömb helyett lehetne multilistát is építeni.
Örülök, hogy inspirált a feladat. Helyesen működik a megoldásod Frici. Ha multilistát építenél, akkor a sok-sok szövegbővítő (
+=
) művelet helyett listabővítés (add()
) lehetne, ami hatékonyabb. Ekkor azi
-edik hónap nevét így lenne praktikus előállítanod ajava.time
csomaggal:Month.of(i).getDisplayName(TextStyle.FULL_STANDALONE, new Locale("hu"))
Köszönöm Sándor. Ez a csomag jobban tetszik és nem ad többet vissza, mint amire szükség van.
Nézegettem egy kicsit a java.time.LocalDate funkciót. Írtam ezt használva egy metódust, ami listázza a konzolra a 21. századból azokat a hónapokat, amikben előfordul péntek 13:
Java forráskód:
Részlet az eredményből:
Tetszik Móni, mert a hónap ebben a
LocalDate
osztályban már nem 0-bázisú. Így kevesebb a hibalehetőség.Aki gyakorolna: készítsen Sankey-diagramot, amely az évek/hónapok és a péntek 13 „viszonyát” ábrázolja. A kiinduló feladat itt található: Sankey-diagram készítése
Közben R. László – a Java SE szoftverfejlesztő tanfolyamunk egyik hallgatója – megoldotta a feladatot. A Sankey-diagram készítése blog bejegyzés hozzászólásai között található az eredményként kapott kép/diagram.