bjorng

bjorng

Erlang Core Team

Advent of Code 2023 - Day 25

I came up with a solution myself, but it is not fast.

My first brute-force solution simply tried removing all combinations of three edges and check if the resulting graph had two components. That algorithm had quartic complexity and would probably have taken weeks to finish.

My second solution was to remove all combinations of two edges. The third edge to remove can then be found in linear time with a simple algorithm based on DFS. That algorithm has “only” cubic complexity, which allowed it to finish in “only” one hour and twenty minutes.

Most Liked

midouest

midouest

I implemented Karger’s algorithm for finding the min-cut and then applied it repeatedly until the cut size was 3:

Part 1
defmodule Part1 do
  def parse(input) do
    for line <- String.split(input, "\n", trim: true),
        reduce: %{} do
      acc ->
        [a | rest] = String.split(line, [":", " "], trim: true)

        for b <- rest,
            reduce: acc do
          acc ->
            acc
            |> Map.update(a, MapSet.new([b]), &MapSet.put(&1, b))
            |> Map.update(b, MapSet.new([a]), &MapSet.put(&1, a))
        end
    end
  end

  def group_product(graph) do
    Map.keys(graph)
    |> Enum.map(&String.length/1)
    |> Enum.map(&div(&1, 3))
    |> Enum.product()
  end

  def cut_size(g1, g0) do
    [as, bs] =
      for key <- Map.keys(g1) do
        key
        |> String.to_charlist()
        |> Enum.chunk_every(3)
        |> Enum.map(&to_string/1)
      end

    bs = MapSet.new(bs)

    for a <- as,
        _ <- MapSet.intersection(g0[a], bs),
        reduce: 0 do
      acc -> acc + 1
    end
  end

  def contract(graph) when map_size(graph) == 2, do: graph

  def contract(graph) do
    {a, ea} = Enum.random(graph)
    b = Enum.random(ea)
    eb = graph[b]

    ab = a <> b

    ea = MapSet.delete(ea, b)
    eb = MapSet.delete(eb, a)

    eab = MapSet.union(ea, eb)

    graph =
      Enum.reduce(ea, graph, fn c, graph ->
        Map.update!(graph, c, fn ec ->
          ec
          |> MapSet.delete(a)
          |> MapSet.put(ab)
        end)
      end)

    graph =
      Enum.reduce(eb, graph, fn c, graph ->
        Map.update!(graph, c, fn ec ->
          ec
          |> MapSet.delete(b)
          |> MapSet.put(ab)
        end)
      end)

    graph =
      graph
      |> Map.delete(a)
      |> Map.delete(b)
      |> Map.put(ab, eab)

    contract(graph)
  end
end

g0 = Part1.parse(input)

Stream.repeatedly(fn -> Part1.contract(g0) end)
|> Enum.find(fn g1 -> Part1.cut_size(g1, g0) == 3 end)
|> Part1.group_product()

Now I just need to finish the other puzzles so that I can complete part 2!

Where Next?

Popular in Challenges Top

sasajuric
Note: This topic is to talk about Day 12 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New
bjorng
Note: This topic is to talk about Day 25 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
bjorng
Note: This topic is to talk about Day 9 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can join...
New
bjorng
This topic is about Day 1 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adven...
New
code-shoily
Just did part 1. Part 2 seems to be demanding too much of my reading time so will get to that after I am done with some chores. Oh here ...
New
bjorng
Here is my solution for day 1 of Advent of Code: defmodule Day01 do def part1(input) do all = parse(input) {first, second} = E...
New
adamu
Probably not the most efficient implementation, because part 1 took &gt;1 ms and part 2 &gt;4ms, but the code was simple enough. def p...
New
mattbaker
I’m having so much fun working on the “Protohackers” challenges, I never got into Advent of Code much but this has been amazing. The chal...
New
Aetherus
Don’t know why the regex ~r/[\W && [^\.]]/x does not work in Elixir. It works pretty well in Ruby. Anyway, here is my solution:
New
Aetherus
I spent 3 hours struggling in part 2, until I noticed a very basic mistake :joy: Here’s my code: By the way, the starting position in...
New

Other popular topics Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

We're in Beta

About us Mission Statement