简体   繁体   中英

How to determine optimal interval count in a given range?

I'm trying to determine the optimal solution for this tough problem. I've got a length (let's say 11). So it's a one dimensional space 0-10. Now I've got these intervals with same length (let's assume 2 in this example). Now they're randomly distributed (overlapping, or not). Let me draw an example:

Situation:

|00|01|02|03|04|05|06|07|08|09|10| <- Space (length = 11)

|-----|       
         |-----|
            |-----|
               |-----|
                  |-----|
                           |-----| <- single interval of length = 2

Now the solution needs to find the maximal number of intervals that can fit at once without overlap.

The solution is: 4 intervals

There are three results of 4 intervals:

|00|01|02|03|04|05|06|07|08|09|10|

|-----|  |-----|-----|     |-----| <- result 1
|-----|  |-----|  |-----|  |-----| <- result 2
|-----|     |-----|-----|  |-----| <- result 3

But there are also two more constraints as well.

  1. If there are more results (of best solution, in this case = 4), then the one with the least number of gaps.

  2. If there are more results still the one with the highest minimal length of all its spaces. For example the one with spaces (of length) 2 & 3 has minimal length of space = 2, that is better than 1 & 4 where the minimal length of space is only 1.

So the result 2 has 4 "continual" chunks, the other two have only 3 so the refinement is:

|00|01|02|03|04|05|06|07|08|09|10|

|-----|  |-----------|     |-----| <- result 1
|-----|     |-----------|  |-----| <- result 3

Those two got same space distributions between them, so let's take first one.

The result for the input set is:

Interval count  : 4
Optimal solution: |-----|  |-----------|     |-----|

The algorithm has to work universally for all the space length (not only 11), all interval lengths (interval length is always <= space length) and any number of intervals.

Update:

Problematic scenario:

|00|01|02|03|04|05|06|07|08|09|

|-----|  
         |-----| 
            |-----|     
                        |-----|
|-----|

This is a simple dynamic programming problem.

Let the total length be N and the length of a task be L .

Let F(T) be maximum number of tasks that can be selected from the sub interval (T, N) , then at each unit time T, there are 3 possibilities:

  1. There is no task that starts at T.
  2. There is a task that starts at T, but we do not include it in the result set.
  3. There is a task that starts at T, and we do include it in the result set.

Case 1 is simple, we just have F(T) = F(T + 1) .

In case 2/3, notice that selecting a task that start a T means we must reject all tasks that start while this task is running, ie between T and T + L . So we get F(T) = max(F(T + 1), F(T + L) + 1) .

Finally, F(N) = 0 . So you just start from F(N) and work your way back to F(0) .

EDIT: This will give you the maximum number of intervals, but not the set that fulfils your 2 constraints. Your explanation of the constraints is unclear to me, so I'm not sure how to help you there. In particular, I can't tell what constraint 1 means since all the solutions to your example set are apparently equal.

EDIT 2: Some further explanation as requested:

Consider your posted example, we have N = 11 and L = 2 . There are tasks that start at T = 0, 3, 4, 5, 6, 9 . Starting from F(11) = 0 and working backwards:

  • F(11) = 0
  • F(10) = F(11) = 0 (Since no task starts at T = 10 )
  • F(9) = max(F(10), F(11) + 1) = 1
  • ...

Eventually we get to F(0) = 4 :

T   |00|01|02|03|04|05|06|07|08|09|10|
F(T)| 4| 3| 3| 3| 3| 2| 2| 1| 1| 1| 0|

EDIT 3: Well I was curious enough about this that I wrote a solution, so may as well post it. This will give you the set that has the most tasks, with the least number of gaps, and the smallest minimum gap. The output for the examples in the question is:

  • (0, 2) -> (4, 6) -> (6, 8) -> (9, 11)
  • (0, 2) -> (4, 6) -> (8, 10)

Obviously, I make no guarantees about correctness! :)

private class Task { public int Start { get; set; } public int Length { get; set; } public int End { get { return Start + Length; } }

    public override string ToString()
    {
        return string.Format("({0:d}, {1:d})", Start, End);
    }
}

private class CacheEntry : IComparable
{
    public int Tasks { get; set; }
    public int Gaps { get; set; }
    public int MinGap { get; set; }
    public Task Task { get; set; }
    public Task NextTask { get; set; }

    public int CompareTo(object obj)
    {
        var other = obj as CacheEntry;
        if (Tasks != other.Tasks)
            return Tasks - other.Tasks; // More tasks is better
        if (Gaps != other.Gaps)
            return other.Gaps = Gaps; // Less gaps is better
        return MinGap - other.MinGap; // Larger minimum gap is better
    }
}

private static IList<Task> F(IList<Task> tasks)
{
    var end = tasks.Max(x => x.End);
    var tasksByTime = tasks.ToLookup(x => x.Start);
    var cache = new List<CacheEntry>[end + 1];

    cache[end] = new List<CacheEntry> { new CacheEntry { Tasks = 0, Gaps = 0, MinGap = end + 1 } };

    for (int t = end - 1; t >= 0; t--)
    {
        if (!tasksByTime.Contains(t))
        {
            cache[t] = cache[t + 1];
            continue;
        }

        foreach (var task in tasksByTime[t])
        {
            var oldCEs = cache[t + task.Length];
            var firstOldCE = oldCEs.First();
            var lastOldCE = oldCEs.Last();

            var newCE = new CacheEntry
            {
                Tasks = firstOldCE.Tasks + 1,
                Task = task,
                Gaps = firstOldCE.Gaps,
                MinGap = firstOldCE.MinGap
            };

            // If there is a task that starts at time T + L, then that will always 
            // be the best option for us, as it will have one less Gap than the others
            if (firstOldCE.Task == null || firstOldCE.Task.Start == task.End)
            {
                newCE.NextTask = firstOldCE.Task;
            }
            // Otherwise we want the one that maximises MinGap.
            else
            {
                var ce = oldCEs.OrderBy(x => Math.Min(x.Task.Start - newCE.Task.End, x.MinGap)).Last();
                newCE.NextTask = ce.Task;
                newCE.Gaps++;
                newCE.MinGap = Math.Min(ce.MinGap, ce.Task.Start - task.End);
            }

            var toComp = cache[t] ?? cache[t + 1];
            if (newCE.CompareTo(toComp.First()) < 0)
            {
                cache[t] = toComp;
            }
            else
            {
                var ceList = new List<CacheEntry> { newCE };

                // We need to keep track of all subsolutions X that start on the interval [T, T+L] that
                // have an equal number of tasks and gaps, but a possibly a smaller MinGap. This is
                // because an earlier task may have an even smaller gap to this task.
                int idx = newCE.Task.Start + 1;
                while (idx < newCE.Task.End)
                {
                    toComp = cache[idx];
                    if
                    (
                        newCE.Tasks == toComp.First().Tasks &&
                        newCE.Gaps == toComp.First().Gaps &&
                        newCE.MinGap >= toComp.First().MinGap
                    )
                    {
                        ceList.AddRange(toComp);
                        idx += toComp.First().Task.End;
                    }
                    else
                        idx++;
                }

                cache[t] = ceList;
            }
        }
    }

    var rv = new List<Task>();
    var curr = cache[0].First();
    while (true)
    {
        rv.Add(curr.Task);
        if (curr.NextTask == null) break;
        curr = cache[curr.NextTask.Start].First();
    }

    return rv;
}

public static void Main()
{
    IList<Task> tasks, sol;

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 2 },
        new Task { Start = 3, Length = 2 },
        new Task { Start = 4, Length = 2 },
        new Task { Start = 5, Length = 2 },
        new Task { Start = 6, Length = 2 },
        new Task { Start = 9, Length = 2 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 2 },
        new Task { Start = 3, Length = 2 },
        new Task { Start = 4, Length = 2 },
        new Task { Start = 8, Length = 2 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    tasks = new List<Task>
    {
        new Task { Start = 0, Length = 5 },
        new Task { Start = 6, Length = 5 },
        new Task { Start = 7, Length = 3 },
        new Task { Start = 8, Length = 9 },
        new Task { Start = 19, Length = 1 },
    };

    sol = F(tasks);
    foreach (var task in sol)
        Console.Out.WriteLine(task);
    Console.Out.WriteLine();

    Console.In.ReadLine();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM