Search code examples
julia

Find the extrema on a more general range of parameters


Suppose I have the following v and w as follows:

julia> v = [1:3, 4:6, 1:2, 3:6, 1:6, 1:6];

julia> w = [1:3, 4:6, 1:6, 1:6, 1:6, 1:4, 5:6, 1:6];

and I want a function to return the minimum and the maximum which would cover all the values in the ranges, i.e. (1,6) in this case.

Currently, I've tried:

flatextrema(q) = 
  foldl((r,b)->
    (min(first(r),first(b)),max(last(r),last(b))), q; 
    init=(first(first(q)),last(first(q))))

flatextrema(q,r...) = 
  flatextrema((flatextrema(q),flatextrema.(r)...))

which gives me:

julia> flatextrema(v,w)
(1, 6)

Is there some nicer/shorter/faster way?

This version of flatextrema takes quite a wide range of possible parameters and returns a tuple.

Another example would be:

julia> flatextrema([1:3,2:4,3:5],[-3:3,1:4],10)
(-3, 10)

The idea is to get a minimum and a maximum which would include all scalars in the parameters.


Solution

  • You like any of these?

    v = [1:3, 4:6, 1:2, 3:6, 1:6, 1:6]
    w = [1:3, 4:6, 1:6, 1:6, 1:6, 1:4, 5:6, 1:6]
    
    function flatextrema1(vs...)
       all_ranges = Iterators.flatten(vs)
       minimum(minimum.(all_ranges)), maximum(maximum.(all_ranges))
    end
    
    function flatextrema2(vs...)
       all_extrema = extrema.(Iterators.flatten(vs))
       minimum(first.(all_extrema)), maximum(last.(all_extrema))
    end
    
    function flatextrema3(vs...)
       all_extrema = extrema.(Iterators.flatten(vs))
       extrema(Iterators.flatten(all_extrema))
    end
    
    # flatextrema = flatextrema1
    # flatextrema = flatextrema2
    flatextrema = flatextrema3
    
    flatextrema(v, w) # (1, 6)
    flatextrema(v, w, 10) # (1, 10)
    

    May be some cool way to do this with JuliaFolds/Transducers.jl - I need to check that package out properly.


    Edit (after benchmark comment):

    Ok here's a faster version

    function flatextrema4(vs...)
       mn,mx = typemax(Int), typemin(Int)
       for cc in vs, c in cc
          cmn, cmx = extrema(c)
          mn, mx = min(mn, cmn), max(mx, cmx)
       end
       return mn, mx
    end
    

    Edit 2 (post accept): Bonus Transducer version (pretty fast and concise)

    using Transducers
    function flatextrema5(vs...)
       foldxl(ProductRF(min, max), vs |> Cat() |> Map(extrema))
    end
    

    I will note that while these solutions handle the amount of nesting in your examples, none of them are generic enough to handle arbitrary nesting. To do that you'd need a more generic, and likely slower, flattening step.