任务占用时间的算法

时间:2013-04-17 14:10:15

标签: c# asp.net algorithm

Edit:
Steps:
Start at target day. 
Then move backwards until no events are carried over from another day.
From there, start counting hours, and keep track of carried over hours. 
Day cannot last more than ActualDayLength()
Then, once you know that, work your way back to target and then calculate actual occupied hours.

我有日历上的任务:

enter image description here

现在让我给出一些背景信息: 每天'持续'在这里7.5小时。但是我使用了一个名为DayHours的变量(现在是7.5)。 (DayHours也用于下面描述的锁定时间)。

此日历的目标是为员工安排7.5小时的工作日。

我需要的是一种算法,可以正确地告诉我一天中实际占用了多少小时。

这看似简单,但实际上非常递归。

首先,几个笔记。你会发现案件经理在14小时内可以在​​7.5小时的2天内完成,剩下1小时。它被延长到3天,因为1.计划,是5个小时,并且2.在当天的前任任务完成之前无法启动。

还有锁定时间的概念。 在紫色是锁定时间。这是10个小时的锁定时间。 这意味着,在12日,我只能做(7.5 - 7.5)小时的工作,而星期一只能做(7.5 - 2.5)。

我已经有了计算实际日期可用时间的功能来解释这个问题:

public decimal GetActualDayLength(DateTime day, Schedule s)
{
    var e = Schedules.GetAllWithElement();
    var t = Timeless(day);
    var locked = from p in e
                 where p.EmployeID == s.EmployeID &&
                 ((p.DateTo.Value.Date) >= t &&
                 Timeless(p.DateFrom.Value) <= t) &&
                 p.IsLocked
                 select p;

    decimal hrs = 0.0M;

    foreach (var c in locked)
    {
        if (c.Hours.Value <= DaysManager.GetDayHours())
            hrs += c.Hours.Value;
        else if (Timeless(c.DateTo.Value) != t)
            hrs += DaysManager.GetDayHours();
        else
        {
            if (c.Hours.Value % DaysManager.GetDayHours() > 0)
                hrs += c.Hours.Value % DaysManager.GetDayHours();
            else
                hrs += DaysManager.GetDayHours();
        }
    }

    return DaysManager.GetDayHours() - hrs;
}

还有携带时间的概念。

以下是一个例子:

enter image description here

现在让我们在18日星期四(18日有1.案例):

要查找当天为该员工提供的小时数,我们需要先查看当天开始,结束或落下的任务。

我不知道18日我可以做多少小时,因为那天结束的任务可能有几个小时。 所以我去看看Perform unit test的开始日。我无法弄清楚这一点,因为NWDM当天结束了,而且它可能有几个小时。

所以现在我去评估NWDM。啊,这一天没有结束,所以我知道附表将需要5 / 7.5小时。

所以我继续前进,每天增加7.5小时。

然后我去了NWDM的最后一天。 直到那时,我工作了5 + 7.5 + 7.5 + 7.5小时,

所以我放了27.5个小时,所以我会在22日(30 - 27.5 = 2.5h)完成它。所以我还有5个小时的时间来完成执行单元测试。

这意味着我需要1.5小时才能完成它。现在案件长达1小时。

如果情况是7.5 - 1.5或更高,我们说这一天已满并返回DayHours。

因此,我们完成了。返回值为1.5 + 1 = 2.5。

该功能看起来应该像这样:

public decimal GetHours(IEnumerable<Schedule> s, DateTime today)
{
    DateTime t = Timeless(today);

    decimal hrs = 0;
    foreach (Schedule c in s)
    {
        if (c.Hours.Value <= DaysManager.GetDayHours())
            hrs += c.Hours.Value;
        else if (Timeless(c.DateTo.Value) != t)
            hrs += DaysManager.GetDayHours();
        else
        {
            if (c.Hours.Value % DaysManager.GetDayHours() > 0)
                hrs += c.Hours.Value % DaysManager.GetDayHours();
            else
                hrs += DaysManager.GetDayHours();
        }
    }
    return hrs;
}

要获取在给定日期内开始,结束或落下的事件,我使用:

public IEnumerable<Schedule> GetAllToday(DateTime date, int employeeID, Schedule current)
{
    DateTime t = Timeless(date);
    int sid = current == null ? -1 : current.ScheduleID;

    var e = Schedules.GetAllWithElement();
    return from p in e
           where (((Timeless(p.DateTo.Value) >= t &&
           Timeless(p.DateFrom.Value) <= t &&
           p.EmployeID == employeeID) &&
           (p.IsLocked || (Timeless(p.DateFrom.Value) < t &&
           (sid == -1 ? true : Timeless(p.DateFrom.Value) < current.DateFrom.Value)) ||
           bumpedList.Any(d => d.ScheduleID == p.ScheduleID)) &&
           p.ScheduleID != sid) ||
           ((Timeless(p.DateTo.Value) >= t &&
           (Timeless(p.DateFrom.Value) == t || (Timeless(p.DateFrom.Value) < t &&
           (sid == -1 ? true : Timeless(p.DateFrom.Value) > current.DateFrom.Value))) &&
           p.EmployeID == employeeID) &&
           !p.IsLocked &&
           !bumpedList.Any(d => d.ScheduleID == p.ScheduleID) &&
           p.ScheduleID != sid)) &&
           p.ScheduleID != sid
           select p;
        }

附表有以下相关字段:

DateFrom
DateTo
Hours
EmployeeID

附表看起来像:

[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Schedule")]
public partial class Schedule : INotifyPropertyChanging, INotifyPropertyChanged
{
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    private int _ScheduleID;
    private System.Nullable<System.DateTime> _DateFrom;
    private System.Nullable<decimal> _Hours;
    private System.Nullable<int> _EmployeID;
    private System.Nullable<int> _RecurringID;
    private System.Nullable<int> _Priority;
    private System.Nullable<System.DateTime> _DateTo;
    private bool _IsLocked;
    private System.Nullable<int> _BumpPriority;
    private EntitySet<Case> _Cases;
    private EntitySet<Project> _Projects;
    private EntitySet<Task> _Tasks;
    private EntitySet<Task> _Tasks1;
    private EntityRef<Employee> _Employee;
    private EntityRef<Recurring> _Recurring;

    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnScheduleIDChanging(int value);
    partial void OnScheduleIDChanged();
    partial void OnDateFromChanging(System.Nullable<System.DateTime> value);
    partial void OnDateFromChanged();
    partial void OnHoursChanging(System.Nullable<decimal> value);
    partial void OnHoursChanged();
    partial void OnEmployeIDChanging(System.Nullable<int> value);
    partial void OnEmployeIDChanged();
    partial void OnRecurringIDChanging(System.Nullable<int> value);
    partial void OnRecurringIDChanged();
    partial void OnPriorityChanging(System.Nullable<int> value);
    partial void OnPriorityChanged();
    partial void OnDateToChanging(System.Nullable<System.DateTime> value);
    partial void OnDateToChanged();
    partial void OnIsLockedChanging(bool value);
    partial void OnIsLockedChanged();
    partial void OnBumpPriorityChanging(System.Nullable<int> value);
    partial void OnBumpPriorityChanged();
    #endregion

    public Schedule()
    {
        this._Cases = new EntitySet<Case>(new Action<Case>(this.attach_Cases), new Action<Case>(this.detach_Cases));
        this._Projects = new EntitySet<Project>(new Action<Project>(this.attach_Projects), new Action<Project>(this.detach_Projects));
        this._Tasks = new EntitySet<Task>(new Action<Task>(this.attach_Tasks), new Action<Task>(this.detach_Tasks));
        this._Tasks1 = new EntitySet<Task>(new Action<Task>(this.attach_Tasks1), new Action<Task>(this.detach_Tasks1));
        this._Employee = default(EntityRef<Employee>);
        this._Recurring = default(EntityRef<Recurring>);
        OnCreated();
    }
}

任何人都可以帮我开发一种可以做到这一点的算法吗?

3 个答案:

答案 0 :(得分:1)

即使您的问题非常复杂且解释不清楚,我也会尽力回答。或者更准确地暗示你应该如何分解和解决它(或者我将如何解决它)。

  

我需要的是一种算法,可以正确地告诉我一天中实际占用了多少小时。

首先,如果DateToSchedule值,我就不会发现真正的问题。除非它等于DateFrom + Hours。在这种情况下,它不会反映真实的DateTo,而是反映一些不相关的值。

我认为任何Schedule都是由开始时间DateFrom和持续时间Hours定义的。 DateTo是计算值,高效计算是问题的真正核心。

所以我认为这个功能可以在任何时间范围内获得可用时间非常简单。用伪代码说话:

TimeSpan GetAvailableTime(DateRange range)
    var tasks = FindIntersectingTasks(range)

    ' now the algorithm which finds available hours on given collection 
    ' of tasks
    ' firstly - we need to determine relevant ranges which intersect 
    '           with given range
    var occupiedRanges = New List<DateRange>(tasks.Count)
    for each task in tasks
        var intersection = range.Intersect(
            new DateRange(task.DateFrom, task.DateTo)
        )

        if Not intersection.IsEmpty
            occupiedRanges.Add(intersection)
        end 
    end 

    ' secondly - sort ranges by start so we can easily merge them
    ranges.Sort(range => range.DateFrom)
    var mergedOccupiedRanges = new List(DateRange)

    ' thirdly - merge ranges so that we have collection with 
    '           non-overlaping ranges (and also sorted)
    for each occupiedRange in occupiedRanges
        ' range may merge only it there is non-empty intersection
        if occupiedRange.CanMerge(mergedOccupiedRanges.Last)
             var mergedRange = range.Merge(mergedOccupiedRanges.Last)
             mergedOccupiedRanges.RemoveLast()
             mergedOccupiedRanges.Add(mergedRange)
        end
    end 

    ' fourthly - it is simple now to determine available/occupied hours
    var timeAvailable = range.Duration
    for each mergedRange in mergedOccupiedRanges
        timeAvailable -= mergedRange.Duration
    end

    return timeAvailable
end

IEnumerable<Schedule> FindIntersectingTasks(DateRange range)
    return From schedule In allEvents
           Where schedule.DateFrom <= range.To 
               And schedule.DateTo >= range.From
end

您可能需要进行一些调整,因为DateTime需要正常的24小时工作日。

答案 1 :(得分:0)

像这样:

  1. 创建一个有空闲时间的日期列表。
  2. 对于列表中的每个项目,添加可用于任务的最长时间。
  3. 如果项目所需的时间达到0,请停止添加块。

答案 2 :(得分:0)

这不能回答确切的问题,但我建议通过使用一些辅助方法扩展类(对象)来简化逻辑,例如:返回占用天数列表的方法/属性。 如果您无法访问这些类(即它们不是来自您的代码库) - 那么创建新类并映射到这些类。 另外 - .NET DateTime类有一些非常有用的属性和枚举,如“DayOfWeek”和“TimeOfDay”,可能对你有用。