thiagomajesk
Generate all combinations having a fixed array size
Hi, I’m trying to solve a problem where I want to generate all possible combinations from an array, with at most n elements. I was looking at this thread today: Most elegant way to generate all permutations? where this really nice solution was proposed by @OvermindDL1:
def permutations([]), do: [[]]
def permutations(list), do: for elem <- list, rest <- permutations(list--[elem]), do: [elem|rest]
So, I was trying to adapt this code to generate combinations with a fixed number of elements.
Example: Given the array [1, 2, 3, 4] and a maximum size of 2, all possible combinations¹ would be:
[1, 2],
[1, 3],
[1, 4],
[2, 1],
[2, 3],
[2, 4],
[3, 1],
[3, 2],
[3, 4],
[4, 1],
[4, 2],
[4, 3],
¹Just to be clear that: all possible combinations, in this case, means: combining every element with 2 more from the array until all elements have been combined.
PS.: I’m not completely comfortable with Elixir yet. So, forgive me if this is something too obvious to be asking here
.
Most Liked
preciz
iex(2)> for x <- [1,2,3,4], y <- [1,2,3,4], x != y, do: [x, y]
[
[1, 2],
[1, 3],
[1, 4],
[2, 1],
[2, 3],
[2, 4],
[3, 1],
[3, 2],
[3, 4],
[4, 1],
[4, 2],
[4, 3]
]
gregvaughn
List comprehensions offer a cartesian product of elements, but you want to exclude pairs of the same value, which can be achieved with a filter in the comprehension
iex> elements = 1..4
iex> for x <- elements, y <- elements, x != y, do: [x, y]
[
[1, 2],
[1, 3],
[1, 4],
[2, 1],
[2, 3],
[2, 4],
[3, 1],
[3, 2],
[3, 4],
[4, 1],
[4, 2],
[4, 3]
]
edit: haha, sniped by @preciz
gregvaughn
That’s where you want recursion like your original code. I once did a job interview question where I needed to figure out the combinations of N 6 sided dice
defp combinations(dice) do
combinations(dice - 1, (for x <- 1..6, do: [x]))
end
defp combinations(0, acc), do: acc
defp combinations(remaining_dice, acc) do
combinations(remaining_dice - 1, (for x <- 1..6, y <- acc, do: [x | y]))
end
mudasobwa
You might check the implementation generating both lists and streams for both combinations and permutations in my package Formulae.
It’s OSS, so welcome to examine the source.
The implementation is fully based on macros; basically it generates the nested for clauses for lists and nested Stream.transform clauses for the input.
thiagomajesk
I’ve found this solution on the web that does exactly what I need. Even though I’m still trying to wrap my head around on how this works as it works (I guess it gets easier to apply these concepts along the way!?)
defp combinations(0, _), do: [[]]
defp combinations(_, []), do: []
defp combinations(size, [head | tail]) do
(for elem <- combinations(size-1, tail), do: [head|elem]) ++ combinations(size, tail)
end
Another thing that is getting me curious about the first code is why changing the return type breaks the result. I mean, when the list is over (already pattern-matched), why does it matter returning [[]] over []?
# Calling: Permute.permutations [1,2,3]
# Returns: [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
defmodule Permute do
def permutations([]), do: [[]] # <-- (!)
def permutations(list), do: for elem <- list, rest <- permutations(list--[elem]), do: [elem|rest]
end
# Calling: Permute.permutations [1,2,3]
# Returns: []
defmodule Permute do
def permutations([]), do: [] # <-- (!)
def permutations(list), do: for elem <- list, rest <- permutations(list--[elem]), do: [elem|rest]
end
Update: Whild guess: Maybe it breaks because for needs something to iterate over when permutations(list--[elem]) reduces the list?







