Daniel
Daniel

Reputation: 6491

Algorithms - Find duration of overlapping intervals in a cyclic world (24 hours)

I've been trying to figure out the algorithm for finding the number of overlapping hours between two time ranges, for example:

enter image description here

Should return 12.

And

enter image description here

Should return 4.

So please help me fill in the gaps in creating the following function:

public static Long findOverlappingInterval(Long startTime1, Long endTime1,
                                           Long startTime2, Long endTime2){ 
    // Any suggestions?
}

Thanks.

EDIT: I'm aware of the solution of creating two binary arrays, using AND and summing up the result. Meaning:

enter image description here

But that would not help my specific needs since i want to use the idea of the algorithm for a solr query, so using arrays and binary operators is not an option for me.

Upvotes: 7

Views: 1368

Answers (5)

Marco13
Marco13

Reputation: 54659

Surprisingly many answers in a short time...

I followed the same idea that was already proposed in other answers: When start time s is smaller than the end time e, then the result can be broken down into two separate computations, for the ranges [s,24] and [0,e].

This can be done "mutually" so there are only 3 simple cases to consider, and the remaining ones can be done with recursive calls.

However, I tried to

  • consider the fact that (according to the images), the end points should be inclusive (!)
  • Add some more test cases
  • Visualize the configurations nicely :-)

This is the result as an MCVE:

public class OverlappingIntervals
{
    private static final long INTERVAL_SIZE = 24;

    public static void main(String[] args)
    {
        test(6,23, 2,17);
        test(0,12, 12,2);

        test(11,4, 12,3);
        test(12,4, 11,3);
    }

    private static void test(
        long s0, long e0, long s1, long e1)
    {
        System.out.println(createString(s0, e0, s1, e1));
        System.out.println(findOverlappingInterval(s0, e0, s1, e1));
    }

    private static String createString(
        long s0, long e0, long s1, long e1)
    {
        StringBuilder sb = new StringBuilder();
        sb.append(createString(s0, e0, "A")).append("\n");
        sb.append(createString(s1, e1, "B"));
        return sb.toString();
    }

    private static String createString(long s, long e, String c)
    {
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<INTERVAL_SIZE; i++)
        {
            if (s < e)
            {
                if (i >= s && i <= e)
                {
                    sb.append(c);
                }
                else
                {
                    sb.append(".");
                }
            }
            else 
            {
                if (i <= e || i >= s)
                {
                    sb.append(c);
                }
                else 
                {
                    sb.append(".");
                }
            }
        }
        return sb.toString();
    }



    public static long findOverlappingInterval(
        long s0, long e0, long s1, long e1)
    {
        return compute(s0, e0+1, s1, e1+1);
    }

    public static long compute(
        long s0, long e0, long s1, long e1)
    {
        if (s0 > e0)
        {
            return 
                compute(s0, INTERVAL_SIZE, s1, e1) +
                compute(0, e0, s1, e1);
        }
        if (s1 > e1)
        {
            return 
                compute(s0, e0, s1, INTERVAL_SIZE) +
                compute(s0, e0, 0, e1);
        }
        return Math.max(0, Math.min(e0, e1) - Math.max(s0, s1));
    }
}

The first two test cases are the ones that have been given in the question, and they properly print 12 and 4, respectively. The remaining two are intended for testing other overlap configurations:

......AAAAAAAAAAAAAAAAAA
..BBBBBBBBBBBBBBBB......
12
AAAAAAAAAAAAA...........
BBB.........BBBBBBBBBBBB
4
AAAAA......AAAAAAAAAAAAA
BBBB........BBBBBBBBBBBB
16
AAAAA.......AAAAAAAAAAAA
BBBB.......BBBBBBBBBBBBB
16

However, note that further test configurations may have to be created in order to cover all possible cases.

Upvotes: 3

Rahul Jain
Rahul Jain

Reputation: 357

Check this link here: Ideone link

EDIT: Inserted the code from the link here:

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static Long min(Long a,Long b){
        return ((a)<(b)?(a):(b));
    }
    public static Long max(Long a,Long b){
        return ((a)>(b)?(a):(b));
    }
    public static void main (String[] args) throws java.lang.Exception
    {
        Long s1=6L,e1=23L,s2=2L,e2=17L,ans=0L;
        Boolean broken1,broken2;
        broken1=(s1<=e1)?false:true;
        broken2=(s2<=e2)?false:true;
        if(broken1){
            if(broken2)
                ans=min(e1,e2)+1 + 23-max(s1,s2)+1;
            else{
                if(e1>=s2) ans+=(min(e1,e2)-s2+1);
                if(s1<=e2) ans+=(e2-max(s1,s2)+1);
            }
        }
        else{
            if(broken2){
                if(e2>=s1) ans+=(min(e1,e2)-s1+1);
                if(s2<=e1) ans+=(e1-max(s1,s2)+1);
            }
            else{
                if(e1<s2 || e2<s1) ans=0L;
                else ans=min(e1,e2)-max(s1,s2)+1;
            }
        }
        System.out.println(ans+"");
    }
}

Upvotes: 0

Arturo Menchaca
Arturo Menchaca

Reputation: 15982

You can do it without creating an array, just calculating intersections between intervals. There are three cases:

  1. No splitted intervals.
  2. One splitted interval.
  3. Both intervals are splitted.

You can solve the problem of splitted intervals as two separate intervals. Using recursion you can do this easily:

public static Long findOverlappingInterval(Long startTime1, Long endTime1, Long startTime2, Long endTime2)
{
    if (startTime1 < endTime1 && startTime2 < endTime2) 
        return Math.max(0, Math.min(endTime2, endTime1) - Math.max(startTime1, startTime2) + 1);
    else
    {
        if (startTime1 < endTime1) 
            return findOverlappingInterval(startTime1, endTime1, 0L, endTime2) + 
                   findOverlappingInterval(startTime1, endTime1, startTime2, 23L);
        else if (startTime2 < endTime2) 
            return findOverlappingInterval(0L, endTime1, startTime2, endTime2) + 
                   findOverlappingInterval(startTime1, 23L, startTime2, endTime2);
        else
        {
            return findOverlappingInterval(0L, endTime1, 0L, endTime2) +
                   findOverlappingInterval(0L, endTime1, startTime2, 23L) +
                   findOverlappingInterval(startTime1, 23L, 0L, endTime2) +
                   findOverlappingInterval(startTime1, 23L, startTime2, 23L);
        }
    }
}

Upvotes: 2

Pham Trung
Pham Trung

Reputation: 11294

First, for interval (a, b) which (a > b), we can easily break it into two intervals

(a , 23) and (0, b)

So, the problem become finding the overlapping between (a , b) and (a1, b1) with a <= b and a1 <= b1

So, there are four cases:

  • b < a1 or b1 < a , which means there is no overlapping, return 0
  • a <= a1 && b1 <= b, which means (a, b) contains (a1, b1), return b1 - a1 + 1
  • a1 <= a && b <= b1, which means (a1, b1) contains (a, b), return b - a + 1
  • Last case is (a, b) and (a1, b1) overlapped part of each interval, that overlapping interval is (max(a1, a), min(b, b1))

Upvotes: 1

James Burnell
James Burnell

Reputation: 26

/** Start times must be smaller than end times */
public static int findOverlappingInterval(int startTime1, int endTime1, int startTime2, int endTime2) {
    int overlappingTime = 0;
    int[] time1 = new int[Math.abs(endTime1 - startTime1)];
    for (int i1 = startTime1; i1 < endTime1; i1++) {
        time1[i1 - startTime1] = i1;
    }
    int[] time2 = new int[Math.abs(endTime2 - startTime2)];
    for (int i2 = startTime2; i2 < endTime2; i2++) {
        time2[i2 - startTime2] = i2;
    }

    for (int i = 0; i < time1.length; i++) {
        for (int j = 0; j < time2.length; j++) {
            if (time1[i] == time2[j]) {
                overlappingTime++;
            }
        }
    }
    return overlappingTime;
}

This method should work for what you want it to do. It worked for me. I am unsure about the wrapping part though.

EDIT

/** Returns the overlap between the four time variables */
public static int findOverlappingInterval(int startTime1, int endTime1, int startTime2, int endTime2) {
    int overlappingTime = 0;

    // First time
    int time1Length = 0;
    if (endTime1 < startTime1) {
        time1Length = 24 - startTime1;
        time1Length += endTime1;
    }
    int[] time1;
    if (time1Length == 0) {
        time1 = new int[Math.abs(endTime1 - startTime1)];
        for (int i1 = startTime1; i1 < endTime1; i1++) {
            time1[i1 - startTime1] = i1;
        }
    } else {
        time1 = new int[time1Length];
        int count = 0;
        for (int i1 = 0; i1 < endTime1; i1++) {
            time1[count] = i1;
            count++;
        }
        for (int i1 = startTime1; i1 < 24; i1++) {
            time1[count] = i1;
            count++;
        }
    }

    // Second time
    int time2Length = 0;
    if (endTime2 < startTime2) {
        time2Length = 24 - startTime2;
        time2Length += endTime2;
    }
    int[] time2;
    if (time2Length == 0) {
        time2 = new int[Math.abs(endTime2 - startTime2)];
        for (int i2 = startTime2; i2 < endTime2; i2++) {
            time2[i2 - startTime2] = i2;
        }
    } else {
        time2 = new int[time2Length];
        int count = 0;
        for (int i2 = 0; i2 < endTime2; i2++) {
            time2[count] = i2;
            count++;
        }
        for (int i2 = startTime2; i2 < 24; i2++) {
            time2[count] = i2;
            count++;
        }
    }

    // Overlap calculator
    for (int i = 0; i < time1.length; i++) {
        for (int j = 0; j < time2.length; j++) {
            if (time1[i] == time2[j]) {
                overlappingTime++;
            }
        }
    }
    return overlappingTime;
}

This new code should do what you want. It loops around a 24 hour period.

Upvotes: 0

Related Questions