【问题标题】:intersection of two sets of data两组数据的交集
【发布时间】:2013-07-28 10:13:38
【问题描述】:

在过去的一个半星期里,我一直在研究这个算法,但我无法让它发挥作用。

基本上我有一个时间表(我知道“边界”的时间值) 我有红色部分(人们进出工作场所的运动)。我想知道人们在他们的日程安排中在工作场所度过的时间,我不在乎他们是在工作之前还是之后,或者在午休时间。

你有什么建议吗?关于我可以在这里应用的数学理论或规则?或您看到的类似问题可以指出我吗?我一直很难找到解决方案。任何帮助将不胜感激。

例如:

时间表:
上午 7:30(开始) 下午 12:00(午休)
下午 1:30(结束午餐休息)下午 5:00(结束工作日)

当天的人员流动:
进场:早上 6:50,离场:早上 6:55
进场:上午 7:00,离场:上午 11:45
进场:下午 1:45,离场:下午 5:05

因此,我的预期输出将是一个时间跨度:7:30(它忽略了工作时间之外的工作时间)

【问题讨论】:

    标签: c# algorithm datetime intersection


    【解决方案1】:

    我会将其视为状态机问题。有四种状态:S+W+、S-W+、S+W-、S-W-。 计划时间对应于 S+ 状态,工人在场对应于 W+ 状态。目标是将 S+W+ 中的时间添加到相交时间。

    有效的转换是:

    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+
    

    按时间顺序处理事件,从状态 S-W- 开始。如果两个事件同时发生,则按任一顺序处理。

    在过渡到 S+W+ 时,请注意时间。在从 S+W+ 过渡时,从过渡时间中减去最后记录的时间,并将结果添加到相交时间。

    【讨论】:

      【解决方案2】:

      将一天分成 1440 个一分钟增量。这是您设置的空间。

      • 设置“S”,即计划分钟数,是该空间的一个子集。
      • 设置“W”,即花在工作上的时间,是该空间的一个子集。

      “S”和“W”的交集是该人在他们的日程安排内的时间量(以分钟为单位 - 根据您的需要转换为 hh:mm)。

      使用其他集合算法,您可以找到它们应该存在但不存在的时间,等等。

      【讨论】:

      • 很好的答案!。但这是最简单的!!
      【解决方案3】:

      您可能想考虑使用this library,但要小心,它会完全忽略DateTime.Kind,不知道时区,也不尊重夏令时。

      • Utc 种上使用是安全的。
      • 切勿在 Local 种类上使用它。
      • 如果您在Unspecified 种类上使用它,请确保您了解上下文是什么。如果它可能是某个具有 DST 的时区的当地时间,那么您的结果可能正确,也可能不正确。

      除此之外,你应该可以使用它的交集功能。

      【讨论】:

        【解决方案4】:

        听起来 LINQ 应该在这里运行良好。我制作了一个简短的示例,使用我的 Noda Time 库,因为它比 .NET 对“一天中的时间”有更好的支持,但您可以在必要时对其进行调整。

        这个想法基本上是你有两个周期集合,你只对交集感兴趣 - 你可以找到 any 调度周期与 any 的交集移动周期 - 仅使用长度为 0 的周期很容易打折不相交的周期。

        这是完整的代码,确实给出了 7 小时 30 分钟的总时间:

        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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-16
          • 1970-01-01
          • 1970-01-01
          • 2011-12-23
          • 2011-01-24
          • 2014-07-04
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多