Skip to content
master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
md
 
 
src
 
 
 
 
 
 
 
 
 
 
 
 
 
 

CoverRanges

Build Status Codecov

Our task is to partially covering a range of integers from a collection of subranges. More specifically, given a range r (a UnitRange, i.e. integer spacing) and a collection of ranges C, we aim to find the smallest subcollection of C that covers at least K elements of r.

This problem was posed by Jakob Nissen in the random channel of the Julia slack, and here I've implemented a dynamic programming algorithm made by Mathieu Tanneau, which is partly based on the solution to the case when K = length(r) given here: https://stackoverflow.com/a/294419.

The file src/CoverRanges.jl was written as a Literate.jl file, and is rendered in Markdown as the last part of my blog post Learning algorithmic techniques: dynamic programming.

Example

Let's consider r = 1:6, C = [1:1, 2:5, 6:6, 1:3, 4:6] and K = 6. This is just the case of needing an 100% cover of the range. A naive greedy algorithm might take the longest range 2:5. But then to cover 1 and 6, two more ranges would be needed. Instead, the optimum is to take the two ranges of length 3, namely 1:3 and 4:6.

julia> using CoverRanges

julia> r = 1:6
1:6

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

julia> C[solve_min_covering(r, 6, C)]
2-element Array{UnitRange{Int64},1}:
 1:3
 4:6

We can see that's exactly what we get. Let's make it more interesting. We'll tile two copies of C next to each other:

julia> append!(C, [r .+ 6 for r in C])
10-element Array{UnitRange{Int64},1}:
 1:1  
 2:5  
 6:6  
 1:3  
 4:6  
 7:7  
 8:11 
 12:12
 7:9  
 10:12

and let's take r=1:12. Now, if K=8, one can just take the two largest ranges:

julia> r = 1:12
1:12

julia> C[solve_min_covering(r, 8, C)]
2-element Array{UnitRange{Int64},1}:
 2:5 
 8:11

But for K = 10, we'll take the two length-3 ranges to cover the first six, then the length 4 range to complete the partial cover:

julia> C[solve_min_covering(r, 10, C)]
3-element Array{UnitRange{Int64},1}:
 1:3 
 4:6 
 8:11

whereas for K=11, the best we can do is cover all 12 by four sets of length-3 ranges:

julia> C[solve_min_covering(r, 11, C)]
4-element Array{UnitRange{Int64},1}:
 1:3  
 4:6  
 7:9  
 10:12

This package also provides an AccessDict object, which is a dictionary that keeps track of when its elements are retrieved or set. Note that only enough methods are implemented for it to be used as a memoization cache in this package; for other uses, more methods would likely be needed (e.g. getindex and setindex!). This can help provide more detailed information about how the algorithm works.

julia> using CoverRanges: LeftToCover, PartialCover

julia> cache = AccessDict{LeftToCover{Int}, PartialCover{Int}}()
AccessDict{LeftToCover{Int64},PartialCover{Int64},Int64} with 0 entries

julia> solve_min_covering(r, 11, C; cache = cache)
4-element Array{Int64,1}:
  4
  5
  9
 10

julia> total_gets(cache)
7

julia> total_sets(cache)
22

About

Partially cover a range using a collection of ranges

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages