在过去一周半的时间里,我一直在讨论这个算法,我无法让它发挥作用。
基本上我有一个时间表(我知道“边界”的时间价值) 我有红色部分(人们进出工作场所的运动)。我想要的是知道人们在工作场所度过的时间,我不在乎他们是在工作之前或之后,还是在午休时间。
你有什么建议吗?关于我可以在这里应用的数学理论或规则?或者您看到的类似问题可以指向我?我一直很难找到解决方案。任何帮助将不胜感激。
例如:
时间表:
上午7:30(开始)中午12:00(午休时间)
下午1:30(endLunchBreak)下午5:00(endOfWorkday)
人们流动的日子:
IN:早上6:50,OUT:早上6:55
IN:早上7点,OUT:上午11:45
IN:下午1:45,OUT:下午5:05
所以,我的预期输出时间为:7:30(它忽略工作时间以外的工作时间)
答案 0 :(得分:3)
我会将此视为状态机问题。有四种状态: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 +时,从转换时间中减去最后记录的时间,并将结果添加到交叉时间。
答案 1 :(得分:2)
将一天分成1440分钟增加一分钟。这是你的固定空间。
“S”和“W”的交集是指他们在他们的日程安排中的时间(以分钟为单位 - 根据您的需要转换为hh:mm)。
使用其他设置算法,您可以找到它们应该在何时但不是等等。
答案 2 :(得分:0)
您可能希望使用this library,但请注意,它完全忽略DateTime.Kind
,不能识别时区,并且不尊重夏令时。
Utc
种类上使用是安全的。Local
种类上使用它,请确保您了解上下文的含义。如果某个时区的本地时间可能是DST,则结果可能正确也可能不正确。除此之外,您应该能够使用其交叉功能。
答案 3 :(得分:0)
听起来LINQ应该在这里运作良好。我用一个简短的例子,使用我的Noda Time库,因为它比.NET更好地支持“一天中的时间”,但你可以在必要时调整它。
这个想法基本上就是你有两个句点集合,而你只对交集感兴趣 - 你可以找到任何计划周期与任何的交集移动周期 - 通过仅使用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);
}
}