2013-04-17 21 views
8
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. 

Tôi có nhiệm vụ được đặt trên một lịch:Thuật toán hàng giờ bị chiếm đóng trong ngày bởi nhiệm vụ

enter image description here

Bây giờ hãy để tôi cung cấp cho này một số bối cảnh: Mỗi ngày 'kéo dài' 7,5 giờ đây . Nhưng tôi làm việc với một biến gọi là DayHours (mà ngay bây giờ là 7.5). (DayHours cũng được sử dụng trong thời gian bị khóa mà Ill mô tả dưới đây).

Mục tiêu của lịch này là lên lịch 7,5 ngày làm việc cho nhân viên.

Những gì tôi cần, là một thuật toán có thể cho tôi biết chính xác số giờ thực sự bị chiếm đóng trong một ngày.

Điều này có vẻ đơn giản, nhưng thực sự khá đệ quy.

Đầu tiên, một vài ghi chú. Bạn sẽ nhận thấy trường hợp quản lý, tại 14 giờ, có thể được thực hiện trong 2 ngày của 7,5 giờ với 1 giờ còn lại. Nó được kéo dài đến 3 ngày bởi vì 1. Lịch trình, là 5 giờ dài, và 2. không thể bắt đầu cho đến khi nhiệm vụ tiền nhiệm của ngày được hoàn thành.

Ngoài ra còn có khái niệm về Thời gian bị khóa. Màu tím là Thời gian đã khóa. Đây là khoảng thời gian bị khóa 10 giờ. Điều này có nghĩa là, vào ngày 12, tôi chỉ có thể làm (7,5 - 7,5) giờ làm việc, và thứ hai, chỉ (7,5 - 2,5) là tốt.

Tôi đã có một hàm để tính toán giờ có sẵn một ngày thực tế để giải thích cho điều này:

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

Ngoài ra còn có các khái niệm về giờ carry.

Dưới đây là một ví dụ:

enter image description here

Bây giờ chúng ta hãy thứ năm 18 (The 18th có 1. Trường hợp):

Để tìm số giờ trong ngày này có cho nhân viên đó, trước tiên chúng ta cần xem xét các nhiệm vụ bắt đầu, kết thúc hoặc rơi vào ngày đó.

Tôi không biết bao nhiêu giờ tôi có thể làm vào ngày 18 vì nhiệm vụ kết thúc ngày hôm đó có thể đã mang theo giờ. Vì vậy, tôi sẽ xem xét ngày bắt đầu của Perform unit test. Tôi không thể tìm ra điều đó vì NWDM kết thúc ngày hôm đó và nó có thể mang theo giờ.

Vì vậy, bây giờ tôi đi đánh giá NWDM. Ahh, cái này không có gì kết thúc vào ngày hôm đó, vì vậy tôi biết Lịch biểu sẽ mất 5/7,5 giờ.

Vì vậy, tôi tiếp tục, thêm 7,5 giờ mỗi ngày tôi vượt qua.

Sau đó, tôi đến ngày cuối cùng của NWDM. Cho đến lúc đó, tôi đã làm việc 5 + 7,5 + 7,5 + 7,5 giờ trên đó,

Vì vậy, tôi đặt trong 27,5 giờ, vì vậy tôi sẽ đưa vào (30 - 27,5 = 2,5h) vào ngày 22 để hoàn thành. Vì vậy, tôi còn 5 giờ để thực hiện Bài kiểm tra thực hiện đơn vị.

Điều này có nghĩa là tôi sẽ cần 1.5h để hoàn thành. Trường hợp bây giờ là 1 giờ.

Trường hợp đã xảy ra từ 7.5 - 1.5 trở lên, chúng tôi cho biết ngày đó đã đầy và trả lại DayHours.

Vì vậy, chúng tôi đã hoàn tất. Giá trị trả về là 1,5 + 1 = 2,5.

Chức năng nên xem xét một chút như thế này:

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

Để có được những sự kiện bắt đầu, kết thúc, hoặc nằm trong một ngày nhất định, tôi sử dụng:

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

Các Schedule có các trường có liên quan sau:

DateFrom 
DateTo 
Hours 
EmployeeID 

Lịch biểu trông giống như sau:

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

Có ai có thể giúp tôi phát triển thuật toán có thể thực hiện việc này không?

+7

Bạn có thể, cố gắng và ngưng tụ sự cố của mình thành một bộ câu được xác định rõ ràng hoặc ít nhất được đánh dấu rõ ràng không? Tôi mất một chút thời gian để tìm ra câu hỏi thực tế. –

+0

Tôi không chắc chắn cách chỉnh sửa theo cách này ... về cơ bản, bắt đầu vào ngày mục tiêu, sau đó di chuyển về phía sau cho đến khi không có gì được chuyển từ một ngày khác, từ đó, bắt đầu đếm giờ và theo dõi được mang theo giờ. Cũng xem xét rằng chiều dài ngày có thể được rút ngắn do thời gian bị khóa ... Sau đó, một khi bạn biết điều đó, hãy làm việc theo cách của bạn trở lại để nhắm mục tiêu và sau đó tính giờ thực tế chiếm đóng. – jmasterx

+0

Tôi đã cố gắng để đưa điều này vào một số loại bước. – jmasterx

Trả lời

1

Mặc dù câu hỏi của bạn rất phức tạp và không được giải thích rõ ràng, tôi sẽ cố gắng trả lời. Hoặc chính xác hơn gợi ý bạn làm thế nào bạn nên phân hủy và giải quyết nó (hoặc làm thế nào tôi sẽ giải quyết nó).

Điều tôi cần, là một thuật toán có thể cho tôi biết chính xác số giờ thực sự bị chiếm đóng trong một ngày.

Lúc đầu, tôi không thấy vấn đề thực sự trong trường hợp bạn có DateTo giá trị có sẵn cho Schedule. Trừ khi nó bằng DateFrom + Hours. Trong trường hợp này, nó không phản ánh giá trị thực DateTo nhưng thay vào đó là một phần không liên quan.

Tôi sẽ giả định bất kỳ Schedule nào được xác định bằng thời gian bắt đầu DateFrom và thời lượng Hours. DateTo được tính toán giá trị và tính toán hiệu quả là cốt lõi thực sự của vấn đề.

Vì vậy, tôi nghĩ rằng chức năng này nhận được giờ sẵn có trong bất kỳ phạm vi thời gian nào là khá đơn giản. Nói bằng mã giả:

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 

Bạn có thể cần một số điều chỉnh khi DateTime mong đợi ngày 24 giờ bình thường.

0

Giống như ví dụ:

  1. tạo một danh sách ngày có thời gian rảnh rỗi.
  2. cho mỗi mục trong danh sách, thêm lượng thời gian tối đa có sẵn cho tác vụ của bạn.
  3. Nếu thời gian cần thiết cho dự án của bạn đạt 0, hãy ngừng thêm khối.
0

Điều này không trả lời chính xác câu hỏi, nhưng tôi khuyên bạn nên đơn giản hóa logic của mình bằng cách mở rộng các lớp (đối tượng) của bạn bằng một số phương thức trợ giúp, ví dụ: các phương thức/thuộc tính trả về danh sách các ngày bị chiếm đóng. Nếu bạn không thể truy cập các lớp này (tức là chúng không phải từ cơ sở mã của bạn) - sau đó tạo các lớp mới và ánh xạ tới các lớp đó. Ngoài ra -.Lớp DateTime của NET có một số thuộc tính rất hữu ích và các enums như "DayOfWeek" và "TimeOfDay" có thể hữu ích cho bạn.

Các vấn đề liên quan