Cleanest way to generate all combinations of n arrays


#1

What I am trying to get is all the possible values given an array [0, 1, 2] and a number n. The result for n=3 would be:

[0,0,0]
[0,0,1],
[0,0,2]
[0,1,0]
...
[2,2,2]

So basically all the numbers in base 3 given as separate elements in an array.

I have been trying to get this output by using Base.Iterators.product([0,1,2],[0,1,2],[0,1,2])and this does indeed work but I’m having trouble finding a way to be able to do so programatically, as I can’t pass the arguments to product separately.

I tried generating the list of arrays by using repeat([0,1,2], 1, n) and this does give me a matrix of n columns with each being one [0, 1, 2] arrays, but I can’t find a way to pass each of these columns separately as input to product()


#2

splatting?


#3

Let me get you an answer.


#4

I tried this but splatting a 3x3 array does not give you 3 1x3 arrays (and product expects n iterables as argument.

julia> x = repeat([1,2,3],1,3)
3×3 Array{Int64,2}:
 1  1  1
 2  2  2
 3  3  3

julia> collect(Base.Iterators.product(x...))
0-dimensional Array{NTuple{9,Int64},0}:
(1, 2, 3, 1, 2, 3, 1, 2, 3)

julia>

#5

Probably not efficient but one way to do so is built the representation of this ternary number.

So you should do a loop up to 3 ^ n - 1 and for each number build its representation per index.


#6
N = 3
lpad.(string.(0:N^N-1;base=N),N,'0').|>s->Int.(collect(s).-48)

Not the most efficient :slight_smile:


#7

This is a naive Combinations iterator, doing something similar to what bennedich proposed.

import Base.iterate,Base.length
struct Combinations{T}
    itr::Vector{T}
    count::Int64
    itrsize::Int64
    function Combinations(itr::Vector{T},count::Int) where T
        new{T}(itr,Int64(count),length(itr))
    end
end

function iterate(c::Combinations,state::Int64=0)
    if state>=length(c)
        return nothing
    end
    indices=digits(state,base=c.itrsize,pad=c.count)
    [c.itr[i] for i in (indices .+1)],state+1
end

function length(c::Combinations)
    length(c.itr) ^ c.count
end

julia> collect(Combinations([0,1,2],3))
27-element Array{Any,1}:
 [0, 0, 0]
 [1, 0, 0]
 [2, 0, 0]
 [0, 1, 0]
 [1, 1, 0]
 [2, 1, 0]
 [0, 2, 0]
 [1, 2, 0]
 [2, 2, 0]
 [0, 0, 1]
 [1, 0, 1]
 [2, 0, 1]
 [0, 1, 1]
 [1, 1, 1]
 [2, 1, 1]
 [0, 2, 1]
 [1, 2, 1]
 [2, 2, 1]
 [0, 0, 2]
 [1, 0, 2]
 [2, 0, 2]
 [0, 1, 2]
 [1, 1, 2]
 [2, 1, 2]
 [0, 2, 2]
 [1, 2, 2]
 [2, 2, 2]

#8

Here’s how to do it with Iterators.product:

N = 3
reverse.(Iterators.product(fill(0:N-1,N)...))[:]

#9

Recursive lambda comprehension:

N = 3
(p=(a,c)->c<2 ? a : [[x;y] for x=a for y=p(a,c-1)])(0:N-1,N)

#10

Here’s a way using digits:

fun(arr::Vector, num::Int) = 
    [ getindex.(Ref(arr), 1 .+ digits(i-1; base=length(arr), pad=num)) for i=1:length(arr)^num ]

fun(["oh","1","2"],3)

#11

Briefer:

N = 3
reverse.(digits.(0:N^N-1,base=N,pad=N))

#12

You can also try:

Iterators.product(ntuple(i->0:N-1, N)...)