如何很好地,高效地计算多个时间间隔之间的总重叠量?

时间:2020-03-13 15:45:16

标签: algorithm math time intervals overlap

我有两个时间窗口:白天每天从07.00持续到19.00,晚上每天从19.00持续到7.00。然后有给定的开始和结束时间[s,e]的“维护间隔”。 s和e是实数,表示从第一天的午夜起算的小时数。对于任何维护间隔,我想确定它是白天最大的部分还是夜间最大的部分。

因此,我开始尝试确定维护间隔在白天的总时间和维护间隔在夜间的总时间,但是我找不到如何很好地做到这一点的方法。

一些输入和输出:

  1. [20,22] =夜间2小时,白天0小时(因此应将其归为夜间维护间隔)
  2. [10,25] =白天9个小时,夜间6个小时(白天维护间隔)
  3. [10,49] =白天21个小时,夜间18个小时(白天维护间隔)

还要观察2和3之间的相似性。第三个间隔持续的时间更长(比第二个间隔长一天),但是结果是相同的。一个不错的解决方案可能会从此特性中受益,它会丢弃所有与最终解决方案无关紧要的“全天候”。

优选地,我获得了一种优雅的解决方案,可以轻松地以数学符号(而不是整个算法)来显示。

希望任何人都能提供帮助!

2 个答案:

答案 0 :(得分:0)

开始总是标准化的,但是结束需要标准化-只是减去开始后整整几天。现在e不超过48

e = e - ((e - s) div 24) * 24

现在我们有s的三个变体,以及每个变体的e的三个可能范围:

 s    0..7                 7..19                   19..24
     -----------------------------------------------------
 e    s..7                 s..19                   s..31
      7..19                19..31                  31..43 
      19..s+24 (<31)       31..s+24 (<43)          43..s+24 (<48)

设置if条件并不难-较长但易于阅读

d = 0
n = 0
if s <= 7:
    if e <= 7:
       n += e - s
    elif e <= 19:
       n += 7 - s
       d += e - 7
    else   
       n += 7 - s + e - 19
       d += 12
 elif s <= 19:
    if e <= 19:
       d += e - s
    elif e <= 31:
       d += 19 - s
       n += e - 19
    else   
       d += 19 - s + e - 31
       n += 12
 else
    if e <= 31:
       n += e - s
    elif e <= 43:
       n += 31 - s
       d += e - 31
    else   
       n += 31 - s + e - 43
       d += 12

现在挤压类似的操作(未彻底检查):

nd = [0, 0]     # night and day hours
halfdays = (s + 5) // 12   
# 0 for the 1st variant, 1 for the 2nd, 2 for the 3rd

halfhours =  halfdays * 12   #time shift
s -= halfhours
e -= halfhours

cur = halfdays & 1
next = 1 - cur
if e <= 7:
       nd[cur] += e - s
    elif e <= 19:
       nd[cur] += 7 - s
       nd[next] += e - 7
    else   
       nd[cur] += e - s - 12
       nd[next] += 12

答案 1 :(得分:0)

这可能比效率更高。除非间隔持续数年,否则您不必担心效率,甚至在这种情况下也不必担心。

让我们首先用好名字声明一些常量。我的代码是Java,但是要翻译成您选择的编程语言应该不难。

private static int HOURS_PER_DAY = 24;
private static double DAY_BEGINS = 7; 
private static double NIGHT_BEGINS = 19;

现在我的算法如下:

    // Maintenance interval to calculate;
    // numbers are numbers of hours since start of the day where the shift begins
    double start = 10;
    double end = 49;

    if (start < 0 || start >= 24 || end < start) {
        throw new IllegalStateException();
    }

    double nightHours = 0;
    double dayHours = 0;
    double time = start;
    double nextNightBegins = NIGHT_BEGINS;
    double nextDayBegins = DAY_BEGINS;
    if (time >= DAY_BEGINS) {
        nextDayBegins += HOURS_PER_DAY;
        if (time < NIGHT_BEGINS) { // time is in day time
            // establish loop invariant
            dayHours += NIGHT_BEGINS - time;
            time = NIGHT_BEGINS;
        }
        nextNightBegins += HOURS_PER_DAY;
    }
    // Loop invariant:
    // time <= nextDayBegins < nextNightBegins || time == end.
    // Hours up to time have been summed into dayHours and nightHours.
    while (time < end) {
        assert time <= nextDayBegins : "" + time + " >= " + nextDayBegins;
        assert nextDayBegins < nextNightBegins;
        double nightHoursUntil = Math.min(nextDayBegins, end);
        nightHours += nightHoursUntil - time;
        time = nightHoursUntil;
        nextDayBegins += HOURS_PER_DAY;
        if (time < end) {
            double dayHoursUntil = Math.min(nextNightBegins, end);
            dayHours += dayHoursUntil - time;
            time = dayHoursUntil;
            nextNightBegins += HOURS_PER_DAY;
        }
    }
    assert time == end;

    System.out.format(Locale.ENGLISH,
            "%.2f hours during nighttime, %.2f hours during daytime%n",
            nightHours, dayHours);
    if (nightHours > dayHours) {
        System.out.println("Nighttime maintenance interval)");
    } else if (nightHours < dayHours) {
        System.out.println("Daytime maintenance interval");
    } else { // they are equal
        System.out.println("Undecided maintenance interval)");
    }

如代码所示,输出为:

18.00 hours during nighttime, 21.00 hours during daytime
Daytime maintenance interval

我更喜欢 not 来利用这样一个事实,即您的边界白天和黑夜的长度相等(每个小时12小时)。某天进行了更改,因此夜晚从18:30开始,因此您不希望冒这样的危险,即程序默认开始发出错误的分类。在上面的算法中,您可以更改常量(在示例中从19更改为18.5),代码仍然可以正常工作。

我确实考虑过使用Java的LocalTime,但是LocalTime最多只能使用23:59:59.999999999,因此无法立即使用。

数学答案

您要查询的内容:我的想法是计算与我定义为间隔时间开始和结束于00:00的“标准情况”相比,夜间的小时数比白天的小时数少多少。对于此计算,我们可以安全地计算结束时间的余数模24,因为它为我们提供了正确的一天时间(夏季时间转换和此类异常除外)。因此,将e设置为e % 24并定义一个函数nMinusD,该函数可以给我们提供与e == 0相比,白天有更多的夜间时间,签名为:

nMinusD(e) = e       for e <= 7
             14 - e  for 7 <= e <= 19
             e - 24  for e >= 19

在纸上绘制曲线可能有助于理解。

对于开始时间,我们只需要反转结果的符号即可。因此,夜间和白天之间的最终签署差异是

diff = nMinusD(e) - nMinusD(s)

现在您可以查看diff的符号。

diff > 0 => more nighttime hours than daytime hours
diff = 0 => equally many nighttime and daytime hours
diff < 0 => more daytime than nighttime hours
相关问题