如何改进此设计以处理相交的日期范围并解决日期范围冲突?

发布于 2024-08-28 20:58:47 字数 1432 浏览 2 评论 0原文

我正在编写一些处理日期范围的代码。我的定价活动有一个开始日期和一个结束日期,以便为该范围设置一定的价格。有多个具有交叉日期范围的定价活动。

我最终需要的是能够查询日期范围内的有效价格。我传入 (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…..
    }
}

I am working on some code that deals with date ranges. I have pricing activities that have a starting-date and an end-date to set a certain price for that range. There are multiple pricing activities with intersecting date ranges.

What I ultimately need is the ability to query valid prices for a date range. I pass in (jan1,jan31) and I get back a list that says jan1-->jan10 $4 , jan11-->jan19 $3 jan20-->jan31 $4.

There are priorities between pricing activities. Some type of pricing activities have high priority, so they override other pricing activities and for certain type of pricing activities lowest price wins etc.

I currently have a class that holds these pricing activities and keeps a resolved pricing calendar. As I add new pricing activities I update the resolved calendar.
As I write more tests/code, it started to get very complicated with all the different cases (pricing activities intersecting in different ways). I am ending up with very complicated code where I am resolving a newly added pricing activity. See AddPricingActivity() method below.

Can anybody think of a simpler way to deal with this? Could there be similar code somewhere out there?

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…..
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

陌路黄昏 2024-09-04 20:58:48

您可以实现 IComparable 并且 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 是我刚刚发明的),正如您想要的那样。但是,您必须添加一些代码才能首先转到请求范围内的第一个活动,并在请求范围之外的第一个活动处停止。

You could implement IComparable<T> and PricingActivity becomes sortable. I think, if I understand you correctly, that you would need to sort by range start, followed by priority. That way, when you query your sorted list, you get the first activity whose range would start and which has the highest priority. Then you just walk the list until you either: find an activity which starts after the last selected one ends, -or-, an activity with a higher priority than the last selected. When you have had the entire list, you should have selected the highest priority activities at each possible (sub)range interval.

Something like this: (not tested)

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 would contain an ordered list of activities which their applicable ranges (DateRange is just invented by me), just as you wanted. However, you'd have to add some code to first go to the first activity which is within the requested range, and stop at the first activity just outside the requested range.

鸢与 2024-09-04 20:58:47

您正在寻找一个简单的线扫描算法。本质上:

  • 将定价活动的所有起点和终点排序为一系列“事件”。
  • 逐步浏览此数组本质上可以获取发生的每个定价“事件”(任何定价周期的开始或结束)。
  • 按顺序扫描此数组可以让您维护在任何特定日期有效的活动的列表。

因此,对于给定的任何日期范围,您可以:

  1. 从数组的开头开始,使用空的“当前活动集”。
  2. 查看数组中的每个元素;如果它是一个起点,则将该活动添加到当前的集合中;如果它是终点,则从集合中删除该活动。
  3. 继续扫描,直到达到所需的日期范围。
  4. 当您通过“输出范围”时,您可以根据集合中的任何活动生成有效价格;在每个事件步骤重新计算价格。
  5. 当您扫描超过所需范围的末尾时,您就完成了。

这样,您就不需要维护可能 n! 大小的活动交叉集,并且可以使用单个 O(n*a) 生成有效的价目表pass(a 是当前集合中活动的平均数量;如果它很小/恒定,则足够接近线性)。

A simple line sweep algorithm is what you're looking for. Essentially:

  • Sort all of the starting and ending points of your pricing activities into an array of "events".
  • Stepping through this array essentially gets you each pricing "event" as it happens (the beginning or ending of any pricing period).
  • Scanning through this array in order lets you maintain a list of which acivities are in effect at any particular date.

So, given any date range, you can:

  1. Start at the beginning of the array, with an empty "current activities set".
  2. Look at each element in the array; if it's a start point, you add that activity to your current set; if it's an ending point, you drop that activity from the set.
  3. Continue the sweep until you reach your desired date range.
  4. As you're passing through your "output range", you can generate your valid price based on whatever activities are in the set; recalculating prices at every event step.
  5. When you've scanned past the end of your desired range, you're done.

This way you don't need to maintain a potentially n! sized set of activity intersections, and you can generate your valid price list with a single O(n*a) pass (a being the average number of activities in the current set; close enough to linear if that's small / constant).

寂寞笑我太脆弱 2024-09-04 20:58:47

一些建议:-

  1. 从中抽象出 DateTimeRange 。在可重用的 DateTimeRange 类中完成计算重叠、交集和并集的所有工作,而不是在此处。

  2. 不要尝试通过插入、更新和删除更新两个数据结构 - 源信息和解析的日历 - 而是更新源数据,然后使用简单的扫描重新计算解析的信息正如其他人所建议的算法。如果您需要在源数据更改之间缓存已解析的日历,请执行此操作(每当源更改时清除缓存的副本并重新计算),否则每次都重新计算它,因为内存而不是 CPU 通常是当今的瓶颈,优化是您应该做的事情仅当您需要时才这样做。

Some suggestions:-

  1. Abstract DateTimeRange out of this. Do all the work of calculating overlaps, intersections and unions in a re-useable DateTimeRange class rather than here.

  2. Don't try to update two data structures with insert, updates and deletes - both the source information and the resolved calendar - instead update the source data and then recalculate the resolved information using a simple sweep algorithm as others have suggested. If you NEED to cache the resolved calendar between changes to the source data, do that (clear cached copy whenever source changes and recalculate) otherwise just recalculate it every time because memory not CPU is typically the bottleneck these days and optimization is something you should do only if you need it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文