Advent of Code – első nap
Az Advent of Code egy évente megrendezett online kihívás. Mondhatni ez egy programozóknak készített adventi naptár, ahol a kis csokik helyett feladatokat kapunk.
Az első feladat minden évben december 1‑jén, magyar idő szerint reggel 6 órakor válik publikussá. Mivel csak a megoldás eredményét kell megadni az oldalon, a kihívások bármilyen programnyelven teljesíthetők . Egy‑egy feladat 2 részből áll, melyek napról-napra nehezednek.
Új cikksorozat
Rendszeres olvasóink tudhatják, hogy az oldalt ketten szerkesztjük. Egy senior fejlesztő 10+ éves és egy junior fejlesztő 2 éves tapasztalattal a háta mögött. Azt a kihívást találtuk ki (az eredetin felül), hogy az egyes napokat mindketten megoldjuk, valamelyiket pedig közzétesszük. Ki tudjátok találni, ki írta a kódot? Vágjunk is bele!
Feladatleírás – első rész
Épp végzed a napi munkád egy hajón, amikor megszólal a fedélzeti riasztó! Rohansz, hátha tudsz segíteni. Úgy tűnik, a Mikulás egyik manója megbotlott, és véletlenül az óceánba ejtette a szán kulcsait!
Mielőtt észbe kapnál, már egy tengeralattjáróban találod magad, amit a manók készenlétben tartanak az ehhez hasonló helyzetekre. Karácsonyi fények borítják (naná, hogy azok), és még egy kísérleti antenna is van benne, ami segítségével bemérhető lenne a kulcsok helyzete, ha elég magasra tudod növelni a jelerősséget; van egy kis kijelző, ami 0-50 csillagig jelzi az antenna jelerősségét.
Az ösztöneid azt súgják, hogy a karácsony megmentéséhez december 25-ig meg kell szerezned mind az ötven csillagot.
Gyűjtsd össze a csillagokat rejtvények megoldásával! Az adventi naptárban minden nap két rejtvényt teszünk elérhetővé; a második feladvány akkor válik láthatóvá, ha az elsőt sikerül megoldanod. Minden rejtvény megoldása egy csillagot ér. Sok szerencsét!
Amint a tengeralattjáró az óceán aljára ér, automatikusan méréseket hajt végre a közeli tengerfenéken. Egy kis képernyőn jelenik meg a mérés jelentése (a rejtvény bemenete): minden sor a tengerfenék mélységének értéke, ahogy a mérőműszer egyre távolodik a tengeralattjárótól.
Tegyük fel például, hogy a következőképp néz ki a jelentés:
199
200
208
210
200
207
240
269
260
263
Ez azt jelenti, hogy a tengeralattjáróból kifelé pásztázva a szonár 199, 200, 208, 210 stb. mélységeket mért.
Az első feladat az, hogy kitaláljuk, milyen gyorsan növekszik a mélység, csak hogy tudd, mivel van dolgod – soha nem tudhatod, hogy a kulcsokat még mélyebb vízbe hordja-e egy óceánáram, vagy egy hal, vagy bármi.
Ahhoz, hogy meghatározd, milyen gyorsan növekszik a mélység mértéke, számold meg, hányszor nő a mélység az előző méréshez képest (Az első mérés előtt nincs mérés.). A fenti példában a változások a következők:
199 (N/A - nincs előző mérés)
200 (nő)
208 (nő)
210 (nő)
200 (csökken)
207 (nő)
240 (nő)
269 (nő)
260 (csökken)
263 (nő)
Ebben a példában 7 mérés mutatott nagyobb értéket, mint az előző.
Minden részvevő más input-ot kap, így az eredmény is más lehet.
Megoldás
Az inputba kapott file 2000 sort tartalmaz, így, bár ha valakinek nagyon sok ideje van, megszámolhatja ott is, de mi most egy C# megoldást kínálunk nektek.
A kihívás teljesítéséhez készítettünk egy ToolKit-et, amit majd egy másik cikkben mutatunk be, addig is 2 dolgot emelnék ki belőle az egyik az a file beolvasása.
public static class FileReader
{
public static IEnumerable<T> ReadLinesOfFile<T>(string filename) where T : IConvertible
{
using (var file = File.OpenText(filename))
{
string? line;
do
{
line = file.ReadLine();
if (!string.IsNullOrEmpty(line))
{
yield return (T)Convert.ChangeType(line, typeof(T));
}
}
while (line != null);
}
}
}
Az fenti kód soronként beolvassa a paraméterben megadott fájlt és T típusként adja vissza soronként.
A feladat megoldásához végig kell pásztáznunk az inputokon és sorban leellenőrizni, hogy nagyobb-e, mint az előző érték. Azaz, ha i-edik elemet vizsgáljuk, akkor azt az (i-1)-edik elemmel kell összehasonlítani. Innen kézenfekvő, hogy a beolvasott elemeket egy int tömbben tároljuk el, mert onnan könnyű az elemeket indexelve kikérni.
int[] numbers = FileReader.ReadLinesOfFile<int>("inputs\\01.txt").ToArray();
Készítsünk egy segéd metódust, ami a numbers tömbből az (i-1)-edik elemet adja vissza, ha az létezik. Az első elemnek nincs megelőző eleme, így ilyenkor állítsuk be az első elemet.
private int GetPrevious(int[] numbers, int i)
{
if (i == 0)
return numbers[0];
return
numbers[i - 1];
}
Ezután nincs is más dolgunk mint végigiterálni a numbers tömbön és elvégezni az összehasonlítást. Vegyünk fel egy int increasedCount változót, amit akkor növelünk, ha az aktuális mérésnek nagyobb az értéke, mint az előzőé.
int increasedCount = 0;
for (int i=0; i<numbers.Length; i++)
{
var previous = GetPrevious(numbers, i);
var current = numbers[i];
if (current > previous)
increasedCount++;
}
Console.WriteLine(increasedCount);
Feladatleírás – második rész
Ha minden egyes mérést figyelembe veszel, az nem ad olyan jó eredményt, mint vártad: egyszerűen túl sok zaj van az adatokban.
Ehelyett inkább három mérésből álló csúszó ablakok összegeit próbáld meg vizsgálni. Például:
199 A
200 A B
208 A B C
210 B C D
200 E C D
207 E F D
240 E F G
269 F G H
260 G H
263 H
Kezdd az első és a második három elemű mérési ablak összehasonlításával. Az első ablakban a mérések A-val vannak jelölve (199, 200, 208), összegük 199 + 200 + 208 = 607. A második ablak B-vel (200, 208, 210) van jelölve, összegük 618. A második ablakban a mérések összege nagyobb, mint az elsőé, így ezt elkönyveljük növekedésnek.
A cél most az, hogy megszámold, hányszor nő ebben a csúszó ablakban a mérések összege az előző hármas ablak összegéhez képest. Tehát hasonlítsa össze A-t B-vel, majd B-t C-vel, majd C-t D-vel és így tovább. Ha már nincs elég mérés egy új hármas ablak létrehozásához, állítsd le az algoritmust.
A fenti példában az egyes összehasonlítások eredménye a következő:
A: 607 (N/A - nincs előző ablak)
B: 618 (nő)
C: 618 (nem változik)
D: 617 (csökken)
E: 647 (nő)
F: 716 (nő)
G: 769 (nő)
H: 792 (nő)
Itt most 5 olyan összeg van, amelyek nagyobbak, mint az előző összeg.
Hány hármas összeg nagyobb, mint az előző hármas összeg?
Megoldás
Készítsünk egy olyan függvényt ami paraméterben megkapja a numbers tömböt és egy számot, ami a hármas alblak kezdő indexe. Az index01,02 és 03 megkapja az értékét a paraméterben kapott i segítségével, majd ezeket az indexeket ellenőrízzük, hogy benne vannak-e numbers tömb indexelési tartományában. Ha nem, visszatérünk hamissal, ha igen akkor igazzal térünk vissza és kiszámoljuk az ablak értékét.
private static bool TryGetWindowSum(int[] numbers, int i, out int sum)
{
int index01 = i;
int index02 = i + 1;
int index03 = i + 2;
if (index01 <= numbers.Length - 1
&& index02 <= numbers.Length - 1
&& index03 <= numbers.Length - 1)
{
sum = numbers[index01] + numbers[index02] + numbers[index03];
return true;
}
sum = 0;
return false;
}
public void Execute()
{
int[] numbers = FileReader.ReadLinesOfFile<int>("inputs\\01.txt").ToArray();
int increased = 0;
int lastsum = 0;
for (int i = 0; i < numbers.Length; i += 1)
{
if (TryGetWindowSum(numbers, i, out int sum1)
&& TryGetWindowSum(numbers, i + 1, out int sum2))
{
if (lastsum == 0)
lastsum = sum1;
if (sum2 > lastsum)
{
++increased;
}
lastsum = sum2;
}
else
{
break;
}
}
Console.WriteLine(increased);
}
Az első feladat logikáját úgy módosítjuk, hogy az összehasonlításhoz nem simán a tömbből kérjük ki az értékeket, hanem a fenti TryGetWindowSum metódusnak adjuk át a vizsgálni kívánt indexeket. Ha az i vagy i+1-es indexre hamisat ad vissza az ellenőrző metódusunk az azt jelenti, hogy már nem tudunk több hármas ablakot előállítani, így ilyenkor egy break-el kilépünk az algoritmusból.
Szerintetek ez a megoldás a senior vagy a junior fejlesztőtől érkezett?
Holnap érkezünk a második napi feladat megoldásával.
Mindenkinek kellemes Advent of Code időszakot kívánunk. ⭐
2021.12.02. @ 13:00
Szerintem a junior fejlesztőtől. A TryGetWindowSum-ban az index túllépés ellenőrzéséhez szerintem elég 1 feltétel: az index03-at elég vizsgálni, hogy kisebb-e, a másik kettő értelemszerűen kisebb lesz. 🙂
Illetve van még egy észrevételem:
for (int i = 0; i < numbers.Length; i += 1)
Ezt így még nem láttam, i++ -t szoktam használni a ciklusváltozó inkrementálására. Ettől eltekintve, tetszik a gondolkodásmód, ahogyan helper metódusokkal építetted fel a megoldást, átlátható, tiszta kód.