Как улучшить этот дизайн для работы с пересекающимися диапазонами дат и разрешения конфликтов диапазонов дат? - PullRequest
2 голосов
/ 10 апреля 2010

Я работаю над кодом, который работает с диапазонами дат. У меня есть действия по ценообразованию с начальной и конечной датой, чтобы установить определенную цену для этого диапазона. Существует несколько видов ценообразования с пересекающимися диапазонами дат.

В конечном итоге мне нужна возможность запрашивать действительные цены для диапазона дат. Я передаю (jan1, jan31) и получаю список, в котором написано jan1 -> jan10 $ 4, jan11 -> jan19 $ 3 jan20 -> jan31 $ 4.

Есть приоритеты между ценообразованием. Некоторые виды ценовых операций имеют высокий приоритет, поэтому они превосходят другие ценовые операции, а для определенных видов ценовых операций выигрывает самая низкая цена и т. Д.

В настоящее время у меня есть класс, который выполняет эти действия по ценообразованию и ведет разрешенный календарь ценообразования. По мере добавления новых ценовых операций я обновляю разрешенный календарь. Поскольку я пишу больше тестов / кода, он стал очень сложным со всеми различными случаями (ценообразование пересекалось по-разному). Я заканчиваю с очень сложным кодом, где я решаю недавно добавленную ценовую деятельность. См. AddPricingActivity() метод ниже.

Кто-нибудь может придумать более простой способ справиться с этим? Может ли где-нибудь быть похожий код?

Public class PricingActivity()
{
    DateTime startDate;
    DateTime endDate;
    Double price;

    Public bool StartsBeforeEndsAfter (PricingActivity pAct)
    {
        // pAct covers this pricing activity
    }
    Public bool StartsMiddleEndsAfter (PricingActivity pAct){
        // early part of pAct Itersects with later part of this pricing activity 
    }
    //more similar methods to cover all the combinations of intersecting
}
Public Class PricingActivityList()
{
    List<PricingActivity> activities;
    SortedDictionary<Date, PricingActivity> resolvedPricingCalendar;
    Public void AddPricingActivity(PricingActivity pAct)
    {
        //update the resolvedCalendar
        //go over each activity and find intersecting ones 
        //update the resolved calendar correctly
        //depending on the type of the intersection
        //this part is getting out of hand…..
    }
}

Ответы [ 3 ]

2 голосов
/ 10 апреля 2010

Простой алгоритм строчной развертки - это то, что вы ищете. По существу:

  • Сортировка всех начальных и конечных точек ваших действий по ценообразованию в массив "событий".
  • Пройдя через этот массив, вы получаете каждое ценовое «событие», как оно происходит (начало или конец любого ценового периода).
  • Сканирование по этому массиву по порядку позволяет вести список действий, действующих на конкретную дату.

Итак, при любом диапазоне дат вы можете:

  1. Начать с начала массива, с пустым «набором текущих действий».
  2. Посмотрите на каждый элемент в массиве; если это начальная точка, вы добавляете эту активность в свой текущий набор; если это конечная точка, вы отбрасываете эту активность из набора.
  3. Продолжайте развертку, пока не достигнете желаемого диапазона дат.
  4. Когда вы проходите через свой «диапазон выработки», вы можете сгенерировать действительную цену на основе любых действий в наборе; пересчет цен на каждом этапе мероприятия.
  5. Когда вы отсканировали за пределами желаемого диапазона, все готово.

Таким образом, вам не нужно поддерживать набор пересечений активности размером потенциально n!, и вы можете сгенерировать действительный прайс-лист с одним проходом O(n*a) (a - это среднее количество действий в текущий набор; достаточно близко к линейному, если он маленький / постоянный).

1 голос
/ 10 апреля 2010

Некоторые предложения: -

  1. Аннотация DateTimeRange из этого. Проделайте всю работу по вычислению перекрытий, пересечений и объединений в повторно используемом классе DateTimeRange, а не здесь.

  2. Не пытайтесь обновлять две структуры данных с помощью вставки, обновления и удаления - как исходной информации, так и разрешенного календаря - вместо этого обновляйте исходные данные и затем пересчитывайте разрешенную информацию, используя простой алгоритм развертки, как предлагали другие. Если вам НЕОБХОДИМО кэшировать разрешенный календарь между изменениями исходных данных, сделайте это (очищайте кэшированную копию при каждом изменении источника и пересчитывайте), в противном случае просто пересчитывайте его каждый раз, потому что память, а не процессор, как правило, является узким местом в эти дни, и оптимизация - это то, что вам следует сделать только если вам это нужно.

0 голосов
/ 10 апреля 2010

Вы можете реализовать IComparable<T> и PricingActivity станет сортируемым. Я думаю, если я вас правильно понимаю, вам нужно будет отсортировать по началу диапазона, а затем по приоритету. Таким образом, когда вы запрашиваете свой отсортированный список, вы получаете первое действие, диапазон которого будет начинаться и который имеет наивысший приоритет. Затем вы просто гуляете по списку до тех пор, пока не выполните одно из следующих действий: найдите действие, которое начинается после завершения последнего выбранного действия, или действие, имеющее более высокий приоритет, чем последнее выбранное. Когда у вас был весь список, вы должны были выбирать действия с наивысшим приоритетом на каждом возможном (под) интервале интервалов.

Примерно так: (не проверено)

Date selectedStart = Date.MinValue;
PricingActivity selected = sortedList[0];
foreach(PricingActivity act in sortedList)
{
    if (act.Start >= selected.End ||
        act.Priority > selected.Priority)
    {
        // Store the selected activity with the applicable date range.
        selectedActivities.Add(
                new DateRange(selectedStart, Math.Min(selected.End, act.Start)),
                selected);
        // The next activity (act) would start here.
        selectedStart = act.Start;
        selected = act;
    }
}

selectedActivities будет содержать упорядоченный список видов деятельности, для которых их применимые диапазоны (DateRange только что придуман мной), как вы и хотели. Однако вам необходимо добавить некоторый код, чтобы сначала перейти к первому действию, которое находится в пределах запрошенного диапазона, и остановиться на первом действии сразу за запрошенным диапазоном.

...