【问题标题】:Algorithm of intersecting set of infinite Iterators无限迭代器相交集的算法
【发布时间】:2016-06-11 22:06:10
【问题描述】:

我必须实现类似于Outlook Meeting Organizer 的调度算法,其中我有几个人参加会议,并且组织者找到邀请列表中的所有人都可用的时间段。因此,假设我有一个实现以下接口的 3rd 方服务:

interface IAvailabilityProvider
{
     IEnumerable<DateTimeInterval> GetPersonAvailableTimeSlots(
              string personName, DateTime startFrom);
}

日期时间间隔在哪里:

class DateTimeInterval{
   public DateTime Start {get;set;}
   public TimeSpan Length {get;set;}
}

GetPersonAvailableTimeSlots 返回infinite iterator,它会枚举人工作时间的所有时间段,不包括周末和节假日之类的,无限到未来。

我的任务是实现一个函数,该函数采用一组迭代器并返回另一个交叉点的迭代器:

IEnumerable<DateTimeInterval> GetIntersections(
    string[] persons, DateTime startFrom);

它获取所有人可用时隙的迭代器,并在所有这些人都可用时返回相交的时隙。在内部我必须实现以下功能:

IEnumerable<DateTimeInterval> GetIntersections(
    IEnumerable<DateTimeInterval>[] personsAvailableSlots);

【问题讨论】:

  • 第 3 方服务返回的迭代器中的时隙有多大?
  • 您似乎使用了 DateTimeRange 类而没有对其进行定义。这些也应该是 DateTimeInterval 还是其他?
  • 不是我是 C# 专家,但不是你的函数 GetIntersections 试图执行无限量的操作(并且还接收无限量的数据作为输入)。
  • @moreON,谢谢。通过 DateTimeRange 我的意思是 DateTimeInterval - 忘记在任何地方重命名它。固定。
  • @displayName,时隙可以是任意大小。它可能来自 DateTime.MinValue->TimeSpan.MaxValue 以及 5 分钟槽。

标签: c# algorithm iterator infinite-loop


【解决方案1】:

解决方案对我来说似乎很简单。

    static IEnumerable<DateTimeInterval> GetIntersections(IEnumerable<DateTimeInterval>[] personsAvailableSlots)
    {
        var enumerators = personsAvailableSlots.Select(timeline => timeline.GetEnumerator()).ToArray();

        // Intersection is empty when at least one of iterators is empty.
        for (int i = 0; i < personsAvailableSlots.Length; i++) if (!enumerators[i].MoveNext()) yield break;

        while (true)
        {
            // first we ensure that intersection exists at the current state
            // if not so, we have to move some iterators forward
            var start = enumerators.Select(tl => tl.Current).Max(interval => interval.Start);

            foreach (var iter in enumerators)
                while (iter.Current.Start + iter.Current.Length <= start)
                    if (!iter.MoveNext()) yield break;

            // now we check if the interval exists
            var int_start = enumerators.Select(tl => tl.Current).Max(interval => interval.Start);
            var int_end = enumerators.Select(tl => tl.Current).Min(interval => interval.Start + interval.Length);

            if (int_end > int_start)
            {
                //if so, we return it
                yield return new DateTimeInterval()
                {
                    Start = int_start,
                    Length = int_end - int_start
                };

                // and, finally, we ensure next interval to start after the current one ends
                //
                // CAUTION: We are able to move iterators whose current interval have passed only. 
                // We will miss huge spans which cover several intervals in other iterators otherwise.
                //
                // In fact we should move the only inerator - that one wich currently limits the last result
                foreach (var iter in enumerators)
                    while (iter.Current.Start + iter.Current.Length == int_end)
                        if (!iter.MoveNext()) yield break;
            } 
        }
    }

我已经针对几个简单的场景对此进行了测试,希望我不会遗漏一些重要的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-08
    • 1970-01-01
    • 2016-11-19
    • 2010-10-11
    • 2012-01-28
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    相关资源
    最近更新 更多