Dátumtartományok kezelése

dátumintervallumok logó

dátumintervallumok logóAki webáruházat üzemeltet és raktároz, befektet áruk raktározásába, biztosan folyamatosan követi a raktárkészlet (és egyúttal pénzügyei) alakulását különböző lekérdezésekkel. Aki online marketinggel foglalkozik, szintén mérheti/követheti/összevetheti egy-egy reklámkampány eszközeinek (Facebook hirdetés, Google Ads hirdetés, e-mail marketing, Instagram hirdetés, blog) eredményességét, hatékonyságát. Az adatok elemzése mindenképpen része a tervezésnek és folyamatosnak/periodikusnak kell lennie.

Tipikus felmerülő kérdések/problémák

  • Hány offline és/vagy online vásárlás/tranzakció volt eddig az aktuális hónapban?
  • Hogyan változott a raktárkészlet az előző hónapban? Miből kell utánrendelni? Mik a kifutó termékek?
  • A bevétel milyen arányban érkezett offline vagy online vásárlásból az aktuális hónapban?
  • Kik vásároltak az előző negyedévben nyomtatót? Küldjünk nekik e-mailt arról, most 10%-kal olcsóbban rendelhetnek tonert, ha kettőt vesznek!
  • Milyen értékben adtak le rendelést a webáruházban két adott dátum által megadott napon? Például hogyan alakult az utóbbi két Black Friday? Esetleg GLAMOUR-napok, húsvét, hosszú hétvége…
  • Kik azok a rendszeres visszatérő vásárlóink, akik nem vásároltak az előző hónapban?
  • Hogyan alakultak „a számok” az előző két év 3. negyedévében!

Egy webáruház raktárkészletének és számláinak nyilvántartása biztosan adatbázisban tárolódik, így könnyen megfogalmazható SQL lekérdező parancsok segíthetik a fenti kérdésekre/problémákra való válaszadást. Természetesen ezeket a műveleteket okosan ki kell vezetni a felhasználói felületre, hogy könnyen paraméterezhetők legyenek.

Lássunk néhány megoldást! A Java forráskódokból azokat a részeket mutatjuk be, amelyek egy lekérdező parancsba beágyazható dátumokra vonatkozó feltételeket kiírják. A dátumok megjelenítésére rövid formátumot használunk konstansként: SimpleDateFormat SHORT_DATE=new SimpleDateFormat("yyyy-MM-dd");.

Aktuális hónap

Érdemes készíteni két túlterhelt metódust. A paraméter nélküli változat az aktuális napot, a paraméteres változat a megadott napot tekinti maximálisnak és ehhez adja meg az adott hónap első/minimális napját. A két dátumnál az év és hónap megegyezik, a nap többnyire különbözik (ritkán megegyezik). A maxDate nem lehet jövőbeli és teljesül a minDate<=maxDate feltétel.

Előző hónap

Itt is érdemes készíteni két túlterhelt metódust. A paraméter nélküli változat az aktuális napot, a paraméteres változat a megadott napot tekinti kiinduló napnak, és ehhez adja meg az előző hónap első és utolsó napját. A két dátumnál az év és hónap megegyezik, a nap mindig különbözik. Mindkét dátum múltbeli és teljesül a minDate<maxDate feltétel. A megvalósítás kezeli az eltérő hosszúságú hónapokat és a szökőévet is. Ha a kiinduló dátum az adott év első hónapjába esik, akkor az előző hónap az előző év utolsó hónapja (ez most automatikusan teljesül, külön nem kell rá figyelni). Hasznos a dátumobjektum add() metódusa, ami az első paraméterében megadott dátummező alapján a második paraméterében megadott értékkel tudja változtatni a dátumot.

Előző negyedév

Itt is hasznos lehet a két túlterhelt metódus. A paraméter nélküli változat az aktuális napot, a paraméteres változat a megadott napot tekinti kiinduló napnak, és ehhez adja meg az előző negyedév első hónapjának első napját és az előző negyedév utolsó hónapjának utolsó napját. A két dátumnál az év megegyezik, a hónap és a nap mindig különbözik. Mindkét dátum múltbeli és teljesül a minDate<maxDate feltétel. A megvalósítás kezeli az eltérő hosszúságú hónapokat. A szökőév most nem számít. Ha a kiinduló dátum az adott év első negyedévébe esik, akkor az előző negyedév az előző év utolsó negyedéve (erre most külön figyelni kell). A negyedév ( quarter) képletén látszik, hogy épít arra, hogy a dátumobjektumtól elkért hónap ( month) 0 bázisú.

Eredmény

dátumintervallumok eredmény

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 21-24. óra: Objektumorientált programozás, 2. rész kapcsolódik alapvetően, de a két visszakapott dátum használható több programozási tétellel (kiválogatás, szétválogatás) tömbbel, lambda kifejezésekkel kollekciókkal, SQL lekérdező parancsban adatbázis-kezeléshez kötődően.

Péntek 13

Péntek 13

Péntek 13Sokan 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: 2019: szeptember, december. 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:

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 tematikában 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).

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 2019-től 2038-ig szervezve az alábbi eredményt adja:

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.

Kígyókockát készítünk

Kígyókocka

KígyókockaA kígyókocka (snake cube, chain cube) 27 egyforma méretű, egymáshoz képest mozgatható/forgatható kockából áll. A kockákat összeköti egy rugalmas fonal/gumi. Az első és az utolsó kocka egy-egy lapján egy-egy lyuk van. A közbenső kockák hat lapjából kettő lyukas. Fából és műanyagból is készülhetnek. Általában kétféle színnel színezettek a kockák. A cél, hogy a 27 kockát kígyózva „összehajtogatva” a kígyó (lánc) összeálljon egy nagyobb 3x3x3 méretű kockává.

A színek – a játék gyártóitól függően – nehézségi szinteket jelölhetnek (zöld, kék, piros, narancs, lila). Léteznek könnyebben és nehezebben megoldható kígyókockák. Előbbieknél többször fordul elő két egymással szemben lévő lyukas lap a közbenső kockákon, utóbbiaknál gyakoribbak az egymással szomszédos lapokon lévő lyukak. Másképpen: előbbiben több a három hosszú egyenes rész, utóbbi szinte állandóan tekereg. Általában a kocka egyik csúcsából kezdjük a megoldást, de az igazán nehéz játékok esetében a kígyó indulhat akár a kocka egyik lapjának (3×3) középső kockájától is.

Vannak olyan kígyókockák, amelyeknek több megoldása is van, azaz többféleképpen is összeállítható kockává. Ezek részletes ismertetése (típusok, gyártók, színek), a megoldások (statikusan és dinamikusan), irányokat mutató jelölésrendszer (Front, Left, Up, Back, Right, Down) elérhető Jaap Scherphuis – holland puzzle rajongó – weboldalán: Jaap’s Puzzle Page.

Kígyókocka

Olyan Java programot készítünk, amely véletlenszerű kígyókockát állít elő.

Tervezés

Szükséges egy háromdimenziós tömb adatszerkezet a kocka tárolására. Több okból is hasznos, ha a tömb mérete 5x5x5. Egyrészt így indexek 0-tól 4-ig futnak és a tömb közepén lévő 3x3x3-as kocka elemei kényelmesen – mátrixszerűen – indexelhetők 1-től 3-ig. Másrészt a tömb közepén lévő 3x3x3-as kocka minden elemére igaz, hogy egységesen van 26 db érvényesen indexelhető szomszédja. A 125 tömbelemből a széleken lévő 98 elem negatív számokkal feltölthető.

A szokásos i, j, k egységvektor rendszerben (koordináta-rendszerben) gondolkodva, i és j a képernyő síkját, k pedig a mélységet jelenti. A k-val 0-tól 4-ig „szeletelve” a tömböt, öt db négyzetet/mátrixot kapunk az alábbiak szerint. A színes tömbelemek negatív számokkal kerülnek feltöltésre, a kígyó útját határolják mindhárom irányból:

Kígyókocka tervezés

A belül lévő – fehér színű – 3x3x3-as kocka/tömb elemeit kezdőértékként célszerű 0-val feltölteni.

A szomszédos kockák kiválasztása során csak a középen lévő kocka 6 db lapszomszédját kell figyelembe venni. A középen lévő (a következő ábrán nem látszó) kocka három tengely szerinti 2-2-2 szomszédos kockája különböző színekkel jelölt.

Kígyókocka tervezés

Él- és csúcsszomszédság esetén nem tud tekeredni a kígyó. A forduláshoz/tekeredéshez legalább 3 – a kígyóban egymás utáni – kocka szükséges. Az aktuális kockának – pozíciójától függően – legfeljebb 6 lapszomszédja lehet. Ezt csökkenti, ha a kocka valamelyik csúcsban helyezkedik el, illetve menet közben is – ahogyan egyre hosszabb lesz a kígyó – folyamatosan csökken a még szabad elemek száma.

A megoldás során a belül lévő – fehér színű – 3x3x3-as kocka/tömb elemeit kell sorszámozni 1-től 27-ig, jelölve ezzel a kígyó útját. A kezdetben 0-val jelölt szabad elemek végül elfogynak.

Megállapodunk abban, hogy a kígyó az útját az (1, 1, 1) pozícióban kezdi és az 1 sorszámot kapja. Addig kell újabb szomszédos kockákat – egyesével haladva – kiválasztani módszeresen vagy véletlenszerűen próbálkozva, vagy akár visszalépéses algoritmussal is, amíg mind a 27 kiválasztásra kerül.

Megvalósítás

Létre kell hozni a háromdimenziós tömböt példányváltozóként:
int[][][] cube=new int[5][5][5];

A cubeInit() metódus kezdetben feltölti a tömb elemeit. A széleken lévő elemekbe különböző negatív értékek kerülnek, hogy jól megkülönböztethető legyen, melyik ciklus melyik pozíciókért felel. Másképpen is lehetne: például kezdetben minden elem -1, utána a belül lévők felülírhatók 0-val.

Hasznos a cubePlot() metódus, amellyel megjeleníthetők a konzolon a tömb elemei (állapota):

A getNextNeighbour() függvény egydimenziós tömbként ( int[]) visszaadja a paramétereként átvett – x, y, z koordinátával jelölt – kocka egyik kiválasztott szomszédját, amely a kígyó útját jelöli. A kiválasztás történhet módszeresen vagy véletlenszerűen próbálkozva, vagy akár visszalépéses algoritmussal is. A metódus forráskódját most nem részletezem. A metódus felelős a kígyó helyes útvonaláért, azaz a kiválasztás során a kígyó nem rekedhet meg zsákutcában, másképpen nem haraphatja meg saját magát.

A vezérlést a run() metódus végzi el az alábbiak szerint:

Addig fut a ciklus, amíg a kígyó nem tölti ki a 3x3x3-as kockát (másképpen: amíg a kígyó nem éri el a maximális hosszúságot). A tömb állapotát kezdetben és lépésenként is kiíratja a vezérlő metódus a konzolra.

Konzolos eredmény

A konzolos eredmény előállításánál fontos volt, a tömb változásait tudjuk követni. Az összes negatív szám elhagyható lehet a kiírásból, ha meggyőződtünk az implementált algoritmus helyes működéséről. Átlátva a problémát, a megoldás könnyen elállítható egy grafikus és/vagy egy irányokat mutató jelölésrendszer szerint is, például:

Kígyókocka tervezés

Hivatkozások

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. Több alkalommal is tudunk ezzel a feladattal foglalkozni, attól függően, hogy a getNextNeighbour() függvény működését hogyan tervezzük és implementáljuk:

  • A 13-16. óra: Tömbök témakör esetén a szomszédos kockák közül módszeresen – azonos sorrendben haladva a legfeljebb 6 lehetséges szomszéd közül – választjuk ki mindig az elsőt. Ekkor mindig ugyanazt az egyetlen helyes megoldást kapjuk meg.
  • A 17-28. óra: Objektumorientált programozás témakör esetén atipikusan a kivételkezelést használhatjuk vezérlésre úgy, hogy a lehetséges szomszédos kockák közül mindig véletlenszerűen választunk. Ekkor a kígyó önmagába harapását úgy előzzük meg, hogy tömb túlindexelésekor keletkező kivételt benyeljük és újrakezdjük a feladatot mindaddig, amíg találunk egy olyan megoldást, aminek lépései közben nem keletkezik kivétel.
  • Az orientáló modul 9-12. óra: Mesterséges intelligencia témakör esetén véletlenszerű kocka kiválasztási stratégiával rendelkező visszalépéses algoritmust specifikálhatunk és implementálhatunk. Ez lényegesen összetettebb feladat és mindig helyes megoldást ad több lehetséges megoldás közül.

Húsvétvasárnap

Húsvétvasárnap

HúsvétvasárnapA nyugati kereszténység húsvétvasárnapja legkorábban március 22-ére, legkésőbb április 25-re esik. Másképpen: a húsvét mozgó ünnep, azaz nem esik az év ugyanazon napjára minden évben. Az első niceai zsinat 325-ben úgy határozott, hogy legyen a keresztény húsvét időpontja a tavaszi napéjegyenlőség utáni első holdtöltét követő vasárnap.

A húsvét kiszámítására a legismertebb algoritmus Gauss módszere. A Java implementációban az easterGauss() függvény által elfogadható év paramétert életszerűen lekorlátoztam 1900-2099-ig terjedő évekre, valamint a vezérlés az aktuális és a rákövetkező 19 évben ír ki eredményt:

Az algoritmus részletes magyarázata alapján könnyen kiegészíthető úgy, hogy tetszőleges évre, illetve különböző naptárakra is működjön.

A kapott eredmények megtekinthetők:

A feladat – a kivételkezeléstől eltekintve – a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek alkalmához kötődik.

Készítettem egy kipróbálható JavaScript változatot is. A csúszkán a kör mozgatásával megkaphatjuk az aktuális és a következő néhány évben a húsvétvasárnap dátumát.

Húsvétvasárnap a megadott évben:

 

Nemzetközi Pi nap

Pi-logo

Pi-logoA Pi-t (π) mindenki ismeri. Talán sokaknak kedvenc története is van a π-vel kapcsolatosan, amelyet iskolában vagy utazásai alatt szerzett. A π Euklidesz geometriájában a kör kerületének és átmérőjének arányát jelöli. A π irracionális szám, azaz végtelen, nem szakaszos tizedestört; másképpen számjegyei között nincs ismétlődés. A π értékével a hétköznapokban 3,14-dal szokás számolni, de a tudomány területén ennél sokkal pontosabb közelítést szokás alkalmazni. A π közelítése az informatikának köszönhetően akár több millió tizedesjegyig is lehetséges (például: S. Memphill: Pi to 1,000,000 places).

A nemzetközi Pi nap alkalmából (március 14) megvalósítottunk néhány – végtelen összeggel és szorzattal – π közelítésre való képletet, algoritmust Java nyelven.

1. Viète-féle sor

Pi-kozelites-Viete

A módszer néhány eredménye: i=5  esetén 3.140331156954752  (2 tizedesjegyre pontos), i=10 -nél 3.1415914215112  (5 tizedesjegyre pontos), i=11  esetén 3.1415923455701176  (6 tizedesjegyre pontos).

2. Leibniz-féle sor

Pi-kozelites-Leibniz

A módszer néhány eredménye: a 24. lépéstől stabil 1 tizedesjegyre, a 626. lépéstől stabil 2. tizedesjegyre, a 2453. lépéstől stabil 3 tizedesjegyre (hiszen alternál).

3. Wallis-formula

Pi-kozelites-Wallis

A módszer néhány eredménye: A 38. lépéstől 1, a 986. lépéstől 2, a 2650. lépéstől 3, a 16954. lépéstől már 4 tizedesjegyre pontos.

4. Csebisev-sor

Pi-kozelites-Csebisev

A módszer k=10 -re már 8 tizedesjegyig pontos.

A bejegyzéshez tartozó teljes forráskódot – további 8 közelítő módszer implementációjával együtt – ILIAS e-learning tananyagban tesszük elérhetővé tanfolyamaink résztvevői számára.

A feladat a Java SE szoftverfejlesztő tanfolyam szakmai moduljának 5-8. óra: Vezérlési szerkezetek alkalmához kötődik.