Advent of Code

Gute Links und Tutorials könnt ihr hier posten.
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

__blackjack__ hat geschrieben: Montag 4. Dezember 2023, 12:25 Heute ist ja wieder eine einfache Aufgabe. Frage mich ob die auch in Betracht ziehen was Wochentag und was Wochenende ist, bei der Aufgabenstellung.
Ich würde sagen, diese Vermutung ist mit der heutigen Aufgabe klar widerlegt. ;-)

Dafür fand ich gestern etwas frustrierend: Teil 2 habe ich nur gelöst bekommen, indem ich die Eingabedaten semi-händisch analysiert und dann eine Eigenschaft genutzt habe, die ich ihnen nicht sofort angesehen habe. Meine generische Verbesserung des brute force-Ansatzes aus Teil 1 lief mit den echten Eingabedaten zu lange und selbst meine wie beschrieben zusammengehackte Lösung braucht noch über 30 Sekunden. Da bin ich wirklich gespannt auf Eure Lösungen.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@Manul: Der Trick für Tag 8 Part 2 ist, dass man jeden Zyklus nur genau ein Mal durchlaufen muss.

Ich muss aber auch sagen, dass ich bei der Aufgabe extrem viel Glück hatte, nicht über das Problem nachzudenken, sondern einfach die „offensichtliche“ Lösung zu nehmen. Letztes Jahr Tag 17 hatte ein sehr ähnliches Problem, allerdings war da der Input nicht so nett, sodass man den Trick von gestern nicht benutzen konnte.

Und 2020 Tag 13 wurde hier im Thread auch schon diskutiert, das ist auch sehr ähnlich, aber die Aufgabenstellung war so gestellt, dass die „einfache“ Lösung wieder nicht ging.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Manul hat geschrieben: Samstag 9. Dezember 2023, 09:54 Dafür fand ich gestern etwas frustrierend: Teil 2 habe ich nur gelöst bekommen, indem ich die Eingabedaten semi-händisch analysiert und dann eine Eigenschaft genutzt habe, die ich ihnen nicht sofort angesehen habe. Meine generische Verbesserung des brute force-Ansatzes aus Teil 1 lief mit den echten Eingabedaten zu lange und selbst meine wie beschrieben zusammengehackte Lösung braucht noch über 30 Sekunden. Da bin ich wirklich gespannt auf Eure Lösungen.
Ich fand es gestern verglichen mit einigen Tag davor relativ einfach. Nachdem ich Teil zwei zunächst naiv versucht habe, fiel mir dann ein, wie das, was ich brauche, mathematisch heißt (kombiniert mit dem was narpel meinte). Und zu meinem Erstaunen gibt es seit Python 3.9 sogar eine Funktion im "math"-Modul aus der Standardbibliothek dafür.
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

narpfel hat geschrieben: Samstag 9. Dezember 2023, 10:21 @Manul: Der Trick für Tag 8 Part 2 ist, dass man jeden Zyklus nur genau ein Mal durchlaufen muss.
Das ist schon klar. Nur kommt dabei eben eine Eigenschaft aller Zyklen zutage, die das Finden der Lösung dann sehr schnell macht, die ich den Eingangsdaten aber so nicht angesehen hätte und die auch nicht in der Aufgabenbeschreibung erwähnt war. Ich vermute, dass alle individuellen Eingangsdaten diese Eigenschaft haben, kann das aber natürlich nicht überprüfen. Eine Lösung, die in annehmbarer Zeit funktioniert, ohne diese Eigenschaft vorauszusetzen, habe ich leider nicht gefunden.

An die beiden anderen Aufgaben erinnere ich mich auch noch, insbesondere an die mit den Bussen, die ich damals, wenn ich mich richtig erinnere, nur dank eines Tips hier aus dem Forum, ich meine, von @__blackjack__, gelöst bekommen habe.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Manul hat geschrieben: Samstag 9. Dezember 2023, 10:48 Das ist schon klar.
Dann bist du aber schon fast am Ziel, das zu einer Lösung führt, die augenblicklich ein Ergebnis liefert (SPOILER).
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

nezzcarth hat geschrieben: Samstag 9. Dezember 2023, 10:54 Dann bist du aber schon fast am Ziel, das zu einer Lösung führt, die augenblicklich ein Ergebnis liefert (SPOILER).
Danke für den Hinweis, aber wie oben geschrieben habe ich Teil 2 bereits gelöst und dabei natürlich die in Deinem Spoiler vorgeschlagene Methode benutzt. Dass das funktionieren würde, war aber m.E. aus der Aufgabenstellung und den Eingabedaten nicht ersichtlich, sondern konnte nur durch Code-assistiertes Untersuchen der Zyklen herausgefunden werden. Ich hatte erst eine Lösung programmiert, die auch für komplexere Zyklen eine Lösung gefunden hätte, mit den realen Eingabedaten aber zu lange lief. Das war, was ich etwas frustrierend fand.
nezzcarth
User
Beiträge: 1635
Registriert: Samstag 16. April 2011, 12:47

Manul hat geschrieben: Samstag 9. Dezember 2023, 11:19 Danke für den Hinweis, aber wie oben geschrieben habe ich Teil 2 bereits gelöst und dabei natürlich die in Deinem Spoiler vorgeschlagene Methode benutzt.
Dass du das gelöst hast, war mir bewusst, jedoch klang die Laufzeit von 30 Sekunden nicht nach der vorgeschlagenen Methode; die liefert das Ergebnis nämlich unmittelbar, "ohne" merkliche Laufzeit.
Zuletzt geändert von nezzcarth am Samstag 9. Dezember 2023, 11:26, insgesamt 2-mal geändert.
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@Manul: Wenn du den im Spoiler gezeigten Trick benutzt hast, dann kann das eigentlich™ nicht sein, dass deine Lösung 30 Sekunden braucht. Meine Lösung braucht für Part 2 30 Millisekunden.
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

Ich habe noch mal nachgeschaut: Die lange Laufzeit lag an meinem Code zum Analysieren der Zyklen. Der war noch auf den allgemeinen Fall ausgerichtet. Wenn ich den durch einen simpleren ersetze, ohne den Rest des Codes zu ändern, läuft meine Lösung auch deutlich unter einer Sekunde.
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich habe zu Tag 8, Teil 2 noch keine Lösung. Bin gestern nur bis zum Analysieren der Kreise gekommen, was konkret heisst, dass ich mir die mit Graphviz mal visualisiert habe und das nette Muster was da mit den paarweisen Knoten und den „über kreuz“-Verbindungen entsteht, und was mich auch spontan an die Sache mit den Bussen erinnert hat. Und die starke Vermutung geweckt hat, dass R und L eigentlich egal ist wenn man sich das Ende des jeweiligen Kreises und das Ende der RL-Anweisungen anschaut.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

Heute fand ich ziemlich anstrengend, insbesondere den 2. Teil. Ich habe eine funktionierende Lösung, die ich aber noch mal ordentlich aufräumen müsste. Dazu habe ich jetzt aber erst mal keine Lust mehr...
derElch
User
Beiträge: 33
Registriert: Sonntag 25. Februar 2018, 13:14

Ich verzweifel gerade an Tag 08. Selbst bei den Testdaten komme ich auf keinen grünen Zweig.

Code: Alles auswählen

Testdaten 1: 2 Schritte ['AAA', 'CCC'] in RIchtung: ['R', 'L']
Testdaten 2: 4 Schritt ['AAA', 'BBB', 'AAA', 'BBB'] in Richtung: ['L', 'L', 'R', 'R']
Laut Angabe sollten aber 6 Schritte bei Testdaten 2 durchgeführt werden.

Das mein BruteForce Ansatz die Laufzeit ausarten lässt ist wiederum ein anderes Problem.

Hier mein Code:

Code: Alles auswählen

import re
from collections import deque
from pathlib import Path

EXAMPLE_DATA = Path("example_data_a1.txt")
DATA = Path("data.txt")


def create_waypoints(lines):
    waypoints = {}
    for line in lines:
        pattern = re.compile(r'\b\w+')
        begin, left, right = re.findall(pattern, line)
        waypoints[begin] = {'L': left, 'R': right}
    return waypoints


def parse_line(lines: str):
    steps, waypoints = lines.split('\n\n')
    waypoints = waypoints.splitlines()
    waypoints = create_waypoints(waypoints)
    return steps, waypoints


def calculate_result_a(steps: list) -> int:
    return len(steps)


def go_through_steps(steps: str, waypoints: dict) -> list:
    remaining_steps = deque(list(steps))
    target_found = False
    waypoint_value = 'AAA'
    waypoint = waypoints[waypoint_value]
    quantity_steps = []
    while not target_found:
        direction = remaining_steps.popleft()
        quantity_steps.append(direction)

        if waypoint_value == 'ZZZ':
            target_found = True
            quantity_steps.pop(-1)
        if not remaining_steps:
            remaining_steps.extend(['R', 'L'])

        waypoint_value = waypoint[direction]
        waypoint = waypoints[waypoint_value]

    return quantity_steps


def main():
    lines = EXAMPLE_DATA.read_text(encoding="utf-8")
    steps, waypoints = parse_line(lines)
    quantity_steps = go_through_steps(steps, waypoints)
    # result_a = calculate_result_a(quantity_steps)
    print(f"Day 08a: {quantity_steps}")


if __name__ == '__main__':
    main()
derElch
User
Beiträge: 33
Registriert: Sonntag 25. Februar 2018, 13:14

So, nach kurzer GitHub nachschauen bin ich draufgekommen, dass ich die Angabe falsch gelesen habe. Daher ist die Frage von oben hinfällig
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

Heute legt Teil 2 wieder nahe, dass man mit einer brute force-Lösung auf die Nase fallen könnte. Allerdings ist mir nicht ganz klar, wie eine solche brute force-Lösung in diesem Fall aussehen könnte. Alle Lösungen, die mir einfallen, sollten mit großen Zahlen eigentlich kein Problem haben.

Hier meine Lösung für heute:

Code: Alles auswählen

import fileinput

from itertools import combinations

from P import P

GALAXY = "#"

class Map:
  def __init__(self):
    self.dimx = 0
    self.dimy = 0
    self.galaxies = []
    self.corrected = None
    self.empty_rows = []
    self.empty_columns = []

  def add_galaxy(self, x, y):
    self.dimx = max(x, self.dimx)
    self.dimy = max(y, self.dimy)
    self.galaxies.append(P(x, y))

  @classmethod
  def from_fileinput(cls):
    newmap = cls()
    for y,line in enumerate(fileinput.input()):
      for x,char in enumerate(line):
        if char == GALAXY:
          newmap.add_galaxy(x, y)
    return newmap

  def expand(self, age = 2):
    self.empty_rows = [ y for y in range(self.dimy) if not any(galaxy.y == y for galaxy in self.galaxies) ]
    self.empty_columns = [ x for x in range(self.dimx) if not any(galaxy.x == x for galaxy in self.galaxies) ]

    self.corrected = [
      P(
        x + (age - 1) * sum(x > column for column in self.empty_columns),
        y + (age - 1) * sum(y > row for row in self.empty_rows)
      ) for x,y in self.galaxies
    ]

def main():
  cosmos = Map.from_fileinput()

  # part 1
  cosmos.expand()

  print(sum(sum(map(abs, (a-b))) for a,b in combinations(cosmos.corrected, 2)))

  # part 2
  cosmos.expand(1000000)

  print(sum(sum(map(abs, (a-b))) for a,b in combinations(cosmos.corrected, 2)))

if __name__ == "__main__":
  main()
(P ist eine Helferklasse für Vektoren, die ich seit einigen AoCs mit durchziehe.)
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

Nach dem ich einige Tage durch viröse Positivität liegen lassen musste, war die heutige Aufgabe wieder ein gutes Beispiel dafür, dass man mit der Implementation des Beispiels so wie es gezeigt wurde bei Teil 2 nicht mehr weiter kommt. Hier mal mein Code:

Code: Alles auswählen

from sklearn.metrics import pairwise_distances

# Einlesen der Daten
data = [line.strip() for line in open('input.txt')]

# Erstellen einer Liste mit den Koordinaten der Galaxien
galaxies = [(y, x) for y in range(len(data)) for x in range(len(data[0])) if data[y][x] == '#']

# Finden der leeren Zeilen
empty_row_idx = [y for y, row in enumerate(data) if all(char=='.' for char in row)]

# Finden der leeren Spalten
empty_col_idx = [x for x in range(len(data[0])) if all(data[y][x]=='.' for y in range(len(data)))]

#der zu addierende Wert pro leerer Zeile/Spalte
add = 999999

#Erstellen einer Liste mit den expandierten Koordinaten
expanded = []
for y, x in galaxies:
    newy, newx = y, x
    for y_idx in empty_row_idx:
        if y > y_idx:
            newy += add
    for x_idx in empty_col_idx:
        if x > x_idx:
            newx += add
    expanded.append((newy, newx))

# Die Berechnung der Paarweisen Distanz überlassen wir Sklearn
print(int(sum(sum(pairwise_distances(expanded, metric='manhattan')))//2))
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

ThomasL hat geschrieben: Montag 11. Dezember 2023, 21:56 Nach dem ich einige Tage durch viröse Positivität liegen lassen musste, war die heutige Aufgabe wieder ein gutes Beispiel dafür, dass man mit der Implementation des Beispiels so wie es gezeigt wurde bei Teil 2 nicht mehr weiter kommt.
Verstehe ich, ehrlich gesagt, nicht wirklich: Heißt das, Du hattest Teil 1 zuerst anders gelöst? Wie denn? Ich selbst musste in meinem Code aus Teil 1 für Teil 2 nur wenig ändern.

Ansonsten: Schön, dass Du wieder gesund bist und vielen Dank für den Tip mit Sklearn in Deinem Code, das kannte ich noch nicht.
Benutzeravatar
ThomasL
User
Beiträge: 1366
Registriert: Montag 14. Mai 2018, 14:44
Wohnort: Kreis Unna NRW

@Manul: Ich habe zuerst tatsächlich Code geschrieben, der die zusätzlichen leeren Spalten und Zeilen erzeugt und in eine neue Karte eingefügt hat. ;-)
Ich bin Pazifist und greife niemanden an, auch nicht mit Worten.
Für alle meine Code Beispiele gilt: "There is always a better way."
https://projecteuler.net/profile/Brotherluii.png
Benutzeravatar
__blackjack__
User
Beiträge: 13116
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Heute wollten sie Pythonprogrammierer wohl mal langweilen. 😛
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Manul
User
Beiträge: 53
Registriert: Samstag 13. Februar 2021, 16:00

Wer spielt eigentlich noch alles mit? Für heute bin ich kurz vorm Aufgeben, weil meine straight-forward-Lösung schon für Teil 1 mit den Beispieldaten über 2 Stunden braucht und dann auch noch ein mutmaßlich falsches Ergebnis ausspuckt. Ich habe noch keine gute Idee, wie ich hier noch optimieren kann.

Mit am spannendsten fand ich bis jetzt Tag 10. Meine Lösung ist zwar nicht besonders schnell, aber ich bin selbst auf den Algorithmus gekommen, wie man "Innen" von "Außen" unterscheidet. Würde mich interessieren, wie Ihr das gelöst habt und ob's da eine kanonische Methode gibt.

Die restlichen Tage bis hierher habe ich alle gelöst, wobei ich den Schwierigkeitsgrad als sehr unterschiedlich empfunden habe - von "schnell gelöst und es bleibt noch Zeit, sich über eine schicke Ausgabe Gedanken zu machen" bis "ganz schön knifflig und erfordert sehr viel Konzentration" war aus meiner Sicht alles dabei.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Ich bin noch dabei, wenn es die Zeit erlaubt. In den letzten 10 Tagen war es schlecht und für die nächsten schaut es auch durchwachsen aus. Schön fand ich Tag 16, da sich dies physikalisch mittels Objekten schön modellieren ließ. Der Schwierigkeitsgrad der Aufgaben hängt aber generell vom algorithmischen Vorwissen der Teilnehmer ab und der Fähigkeit den richtigen Lösungsweg aus den Aufgaben herauszulesen, da dieser dort gerne etwas versteckt wird. Ob ich bestimmte Aufgaben mache, hängt auch davon ab, ob ich den Lösungsweg bereits sehe, oder mir diesen erst erarbeiten muß. So bin ich z.B. bei kürzesten Wegen durch gewichtete Graphen gar nicht gut, da ich diese Algorithmen nie gebraucht habe und mir das erst wieder aneignen müsste. Und dafür bräuchte es ein paar verregnete Tage an denen ich sonst nichts zu tun habe. Generell sehe ich AoC als interessanten Pool von Übungsaufgaben.
Antworten