Рассчитайте определенные соотношения “работы”, “низкой работы” и “без работы” для больших диапазонов дат.

У меня есть следующий алгоритм для расчета определенных соотношений «работа», «низкая работа» и «отсутствие работы» в течение установленного диапазона дат (startDate, endDate). Алгоритм работает нормально, но когда я увеличиваю диапазон дат, он, очевидно, работает медленнее, потому что ему приходится перебирать каждый день этого диапазона каждую минуту. Есть ли способ улучшить производительность с помощью другого типа цикла или предложения where, чтобы найти текущую дату в listWorkTime.

private List<DateSharedWork> CalculateDateSharedWork(DateTime startDate, DateTime endDate, ICollection<WorkTime> listWorkTime)
        {

      List<DateSharedWork> listDateSharedWork = new List<DateSharedWork>();

      // +1 to include last day at full
      int range = endDate.Subtract(startDate).Days + 1;

      // start at startDate
      Parallel.For(0, range, i =>
      {

        DateTime currDate = startDate.AddDays(i);

        //set minute interval
        double everyNMinutes = 1.0;
        double minutesADay = 1440.0;

        // reset counter
        int work_counter = 0;
        int lowWork_counter = 0;
        int noWork_counter = 0;

        int l = (int)(minutesADay / everyNMinutes);

        for (int j = 0; j < l; j++)
        {

          DateTime check15 = currDate.AddMinutes(j * everyNMinutes);

          // check if listWorkTime includes current date
          var foundTime = listWorkTime.Where(x => check15 >= x.FromDate && check15 <= x.ToDate).ToList();

          if (foundTime.Count(x => x.TimeRangeId == 1) > 0)
          {
            // found interval that is within work hours
            work_counter++;
            noWork_counter++;
          }
          else
          {
            if (foundTime.Count(x => x.TimeRangeId == 2) > 0)
            {
              // found intervall that is within low work hours
              lowWork_counter++;
              noWork_counter++;

            }
          }
        };

        double work = everyNMinutes / minutesADay * work_counter;
        double lowWork = everyNMinutes / minutesADay * lowWork_counter;
        double noWork = 1.0 - (everyNMinutes / minutesADay * noWork_counter);

        listDateSharedWork.Add(new DateSharedWork(currDate, work, lowWork, noWork));

      });

      listDateSharedWork.Sort((x, y) => DateTime.Compare(x.Date, y.Date));

      return listDateSharedWork;

    }

Редактировать

определения классов DateSharedWork и WorkTime

public class DateSharedWork
  {

    public DateSharedWork(DateTime date, double? work = 0.0, double? lowWork = 0.0, double? noWork = 1.0)
    {
      this.Date = date;
      this.Work = work.Value;
      this.LowWork = lowWork.Value;
      this.NoWork = noWork.Value;
    }

    public DateTime Date { get; private set; }
    public double Work { get; private set; }
    public double LowWork { get; private set; }
    public double NoWork { get; private set; }
  }

public class WorkTime
  {
    public int Id { get; set; }
    public DateTime? FromDate{ get; set; }
    public DateTime? ToDate{ get; set; }
    public int? TimeRangeId{ get; set; }
  }

Edit2

начиная с startDate значение как первый день и увеличивая по минутной шкале в течение всего дня, я получаю отношения нормальная работа а также низкая работа на тот день под флагом TimeRangeId,
listWorkTime содержит информацию, когда определенный период был либо рабочим, либо малым, эти периоды могут перекрываться, а также дублироваться, но нормальная работа (TimeRangeId == 1) доминирует

1 ответ
1

Всего несколько быстрых снимков …

  • Вместо того, чтобы иметь DateTime check15 что приводит к предположению, что может быть еще несколько DateTime структуры, лежащие вокруг, такие как check1 … check15, вы могли бы просто использовать currDate который, кстати, следует переименовать в currentDate потому что сокращения затруднят чтение кода. Используя currentDate ты мог бы заменить (j * everyNMinutes в currDate.AddMinutes()просто 1.0 или лучше everyNMinutes.

  • foundTime как имя для List не лучший выбор в качестве List подразумевает более одного элемента. Как насчет foundTimes ? Но гораздо важнее то, что каждую минуту код повторяется listWorkTime в худшем случае дважды. Если вы используете только Count() чтобы проверить, есть ли любой элемент найден, тогда просто используйте Any(). Count() будет перебирать все элементы, пока Any() останавливается на первом найденном элементе.

  • Вы должны использовать профилировщик, чтобы проверить, удаляет ли ToList после Where() получит больше производительности.

  • идеоматический способ именования переменных в C # использует camelCase кожух вместо snake_caseкожух.

  • вместо if внутри else блок, вы должны использовать else if вместо.

  • Что меня беспокоит больше, так это то, что происходит, если в foundTimes который удовлетворяет обоим условиям?

Внедрение указанных изменений без учета последнего пункта выглядит так:

private List<DateSharedWork> CalculateDateSharedWork(DateTime startDate, DateTime endDate, ICollection<WorkTime> listWorkTime)
{

    List<DateSharedWork> listDateSharedWork = new List<DateSharedWork>();

    // +1 to include last day at full
    int range = endDate.Subtract(startDate).Days + 1;

    // start at startDate
    Parallel.For(0, range, i =>
    {

        DateTime currentDate = startDate.AddDays(i);

        //set minute interval
        double everyNMinutes = 1.0;
        double minutesADay = 1440.0;

        // reset counter
        int workCounter = 0;
        int lowWorkCounter = 0;
        int noWorkCounter = 0;

        int l = (int)(minutesADay / everyNMinutes);

        for (int j = 0; j < l; j++)
        {

            currentDate = currentDate.AddMinutes(everyNMinutes);
            // check if listWorkTime includes current date
            var foundTimes = listWorkTime.Where(x => currentDate >= x.FromDate && currentDate <= x.ToDate).ToList();

            if (foundTimes.Any(x => x.TimeRangeId == 1))
            {
                // found interval that is within work hours
                workCounter++;
                noWorkCounter++;
            }
            else if (foundTimes.Any(x => x.TimeRangeId == 2))
            {
                // found intervall that is within low work hours
                lowWorkCounter++;
                noWorkCounter++;
            }

        };

        double work = everyNMinutes / minutesADay * workCounter;
        double lowWork = everyNMinutes / minutesADay * lowWorkCounter;
        double noWork = 1.0 - (everyNMinutes / minutesADay * noWorkCounter);

        listDateSharedWork.Add(new DateSharedWork(currentDate, work, lowWork, noWork));

    });

    listDateSharedWork.Sort((x, y) => DateTime.Compare(x.Date, y.Date));

    return listDateSharedWork;

}

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *