简体   繁体   中英

intersection of two sets of data

I've been cracking my head over this algorithm for the past week and a half and i cant get it to work.

Basically i have an schedule (i know the Time value of the "borders") and i have the red section (peoples movements in and out of the workplace). What i want is to know the time people spend at the workplace WITHIN their schedule, i dont care if they are there before or after work, or in the lunch break.

do you have any suggestions? on a mathematical theory or rule that i can apply here? or a similar problem you have seen you can point me to? i've been having a really hard time finding a solution. Any help would be appreciated.

图片

For example:

Schedule:
7:30am (start) 12:00pm(lunchbreak)
1:30pm(endLunchBreak) 5:00pm(endOfWorkday)

People movements trough the day:
IN: 6:50am, OUT: 6:55am
IN: 7:00am, OUT: 11:45am
IN: 1:45pm, OUT: 5:05pm

So, my expected output would be a timespan of: 7:30 (it ignores time IN workplace outside of work schedule)

I would treat this as a state machine problem. There are four states: S+W+, S-W+, S+W-, SW-. Scheduled time corresponds to S+ states, worker present to W+ states. The objective is to add time in S+W+ to the intersection time.

The valid transitions are:

S+W+ End of schedule -> S-W+
S+W+ Worker leaves -> S+W-
S-W+ Start of schedule -> S+W+
S-W+ Worker leaves -> S-W-
S+W- End of schedule -> S-W-
S+W- Worker arrives -> S+W+
S-W- Start of schedule -> S+W-
S-W+ Worker arrives -> S-W+

Process events in time order, starting in state SW-. If two events happen at the same time, process in either order.

On transition into S+W+, note the time. On transition out of S+W+, subtract the last noted time from the time of the transition, and add the result to the intersection time.

Break the day into 1440 one minute increments. This is your set space.

  • Set "S", the scheduled minutes, is a subset of that space.
  • Set "W", the amount of time spent on the job, is a subset of that space.

The intersection of "S" and "W" is the amount of time the person was there within their schedule (in minutes - convert to hh:mm per your needs).

Using other set algorithms you can find when they should have been there but weren't, etc.

You might want to look into using this library , but be careful, it completely ignores DateTime.Kind , is not time zone aware, and doesn't respect daylight saving time.

  • It is safe to use on Utc kinds.
  • Never use it on Local kinds.
  • If you use it on Unspecified kinds, make sure you understand what the context is. If it could possibly be a local time in some time zone that has DST, then your results may or may not be correct.

Other than that, you should be able to use its intersection function.

It sounds like LINQ should work well here. I've whipped up a short example, using my Noda Time library as it has better support for "time of day" than .NET, but you could adapt it if necessary.

The idea is basically that you have two collections of periods, and you're only interested in the intersection - you can find the intersection of any schedule period against any movement period - it's easy to discount periods that don't intersect by just using a 0-length period.

Here's the complete code, which does indeed give a total time of 7 hours and 30 minutes:

using System;
using System.Collections.Generic;
using System.Linq;
using NodaTime;

class Test
{
    static void Main()
    {
        var schedule = new List<TimePeriod>
        {
            new TimePeriod(new LocalTime(7, 30), new LocalTime(12, 0)),
            new TimePeriod(new LocalTime(13, 30), new LocalTime(17, 0)),
        };

        var movements = new List<TimePeriod>
        {
            new TimePeriod(new LocalTime(6, 50), new LocalTime(6, 55)),
            new TimePeriod(new LocalTime(7, 0), new LocalTime(11, 45)),
            new TimePeriod(new LocalTime(13, 45), new LocalTime(17, 05))
        };

        var durations = from s in schedule
                        from m in movements
                        select s.Intersect(m).Duration;
        var total = durations.Aggregate((current, next) => current + next);
        Console.WriteLine(total);
    }
}

class TimePeriod
{
    private readonly LocalTime start;
    private readonly LocalTime end;

    public TimePeriod(LocalTime start, LocalTime end)
    {
        if (start > end)
        {
            throw new ArgumentOutOfRangeException("end");
        }
        this.start = start;
        this.end = end;
    }

    public LocalTime Start { get { return start; } }
    public LocalTime End { get { return end; } }
    public Duration Duration { get { return Period.Between(start, end)
                                                  .ToDuration(); } }

    public TimePeriod Intersect(TimePeriod other)
    {
        // Take the max of the start-times and the min of the end-times
        LocalTime newStart = start > other.start ? start : other.start;
        LocalTime newEnd = end < other.end ? end : other.end;
        // When the two don't actually intersect, just return an empty period.
        // Otherwise, return the appropriate one.
        if (newEnd < newStart)
        {
            newEnd = newStart;
        }
        return new TimePeriod(newStart, newEnd);
    }
}

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