stevensonmt

stevensonmt

Union Find algorithm help - am having to iterate over all the “parents” a second time

I’m trying to do a very basic union-find (not doing any path compression by rank or size).

    def find(dsu, x) do
      case Map.get(dsu, x) do
        nil ->
            {Map.put(dsu, x, x), x}
        ^x -> {dsu, x}
        y -> 
            {dsu2, p} = find(dsu, y)
            {Map.put(dsu2, x, p), p}
      end
    end

  def union(%{} = dsu, x, y) do 
    {dsu, a} = find(dsu, x)
    {dsu, b} = find(dsu, y)
    if a == b do 
      dsu
    else
      [a,b] = Enum.sort([a,b])
      {dsu, c} = find(dsu, a)
      Map.put(dsu, b, c)
    end    
  end

The issue I’m having is that after iterating over a set of nodes I have to iterate over all the “parents” a second time because the find function isn’t recursing on those.

@spec num_islands(grid :: [[char]]) :: integer
  def num_islands(grid) do
    map = LandMap.new(grid) 
    m = grid |> length() |> Kernel.-(1)
    n = hd(grid) |> length() |> Kernel.-(1)
    
    
    intermediate = 
    0..m
    |> Enum.reduce(%{}, fn x, acc -> 
      0..n
      |> Enum.filter(fn y -> MapSet.member?(map, {x,y}) end)
      |> Enum.reduce(acc, fn y, acc2 ->
        acc3 = DSU.union(acc2, {x,y}, {x,y})
        LandMap.neighbors({x,y}, m, n)
        |> Enum.filter(fn coord -> MapSet.member?(map, coord) end)
        |> Enum.reduce(acc3, fn neighbor, acc4 -> DSU.union(acc4, neighbor, {x,y}) end)
      end)
    end)
    
    intermediate
    |> Map.values()
    |> Enum.reduce(intermediate, fn x, islands -> 
      case DSU.find(islands, x) do 
        {^islands, ^x} -> islands
        {islands, y} -> 
          islands
          |> Enum.map(fn {k, v} = curr -> 
            if v == x do 
              {k, y}
            else
              curr
            end
          end)
          |> Map.new()
        true -> islands
      end
    end)
    |> Map.values()
    |> Enum.uniq()
    |> Enum.count()
  end

If I don’t do the second pass a node that was initially it’s own parent but later linked to others will get updated, but any nodes pointing to it as parent would not be updated. Do I need to track a “parents” map and a “children” map so that I can update any children when a “parent” is updated?

Marked As Solved

hst337

hst337

defmodule DisjoinSets do
  def add(disjoint_sets, entry) do
    Map.put(disjoint_sets, entry, {:root, 0})
  end

  def find(disjoint_sets, entry) do
    with {:ok, root, _rank} <- do_find(disjoint_sets, entry) do
      {:ok, root}
    end
  end

  defp do_find(disjoint_sets, entry) do
    case disjoint_sets do
      %{^entry => {:root, rank}} ->
        {:ok, entry, rank}

      %{^entry => {:parent, parent}} ->
        do_find(disjoint_sets, parent)

      %{} ->
        {:error, :not_present}
    end
  end

  def union(disjoint_sets, left, right) do
    with(
      {:ok, left_parent, left_rank} <- do_find(disjoint_sets, left),
      {:ok, right_parent, right_rank} <- do_find(disjoint_sets, right)
    ) do
      cond do
        left_rank < right_rank ->
          {:ok, Map.put(disjoint_sets, left_parent, {:parent, right_parent})}

        left_rank > right_rank ->
          {:ok, Map.put(disjoint_sets, right_parent, {:parent, left_parent})}

        left_rank == right_rank ->
          disjoint_sets =
            disjoint_sets
            |> Map.put(right_parent, {:parent, left_parent})
            |> Map.put(left_parent, {:root, left_rank + 1})

          {:ok, disjoint_sets}
      end
    end
  end
end

I wrote this simple rank-based disjoint set implementation. I chose verbosity and ease of reading over performance

Also Liked

hst337

hst337

Could you please describe original task and what “union find algorithm” is for?

stevensonmt

stevensonmt

Leetcode Number of Islands problem: https://leetcode.com/problems/number-of-islands/

Also in case you meant what “union find” means, a lot of places refer to it as “disjoint set” I think.

code-shoily

code-shoily

I will look into it after work. It’s surprising how hard on memory disjoint set is despite being a simple algorithm.

I looked into number of island and I think using :digraph and connected component could help?

Back to your DS algorithm I’m interested to trace it but if it helps let me send you an implementation I did: ex_algo/lib/ex_algo/set/disjoint_set.ex at main · code-shoily/ex_algo · GitHub

Thank you for motivating me to look into that again, I’ll be back soon

Where Next?

Popular in Questions Top

skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lists...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement