如何改进此设计以处理相交的日期范围并解决日期范围冲突?
我正在编写一些处理日期范围的代码。我的定价活动有一个开始日期和一个结束日期,以便为该范围设置一定的价格。有多个具有交叉日期范围的定价活动。
我最终需要的是能够查询日期范围内的有效价格。我传入 (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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以实现
IComparable
并且PricingActivity
变得可排序。我认为,如果我理解正确的话,您需要按范围开始排序,然后按优先级排序。这样,当您查询排序列表时,您将获得范围将开始且具有最高优先级的第一个活动。然后,您只需遍历列表,直到找到以下任一活动:找到在最后选择的活动结束后开始的活动,或者-找到比最后选择的优先级更高的活动。当您拥有整个列表时,您应该在每个可能的(子)范围间隔中选择最高优先级的活动。像这样的东西:(未经测试)
selectedActivities
将包含其适用范围的活动的有序列表(DateRange
是我刚刚发明的),正如您想要的那样。但是,您必须添加一些代码才能首先转到请求范围内的第一个活动,并在请求范围之外的第一个活动处停止。You could implement
IComparable<T>
andPricingActivity
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)
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.您正在寻找一个简单的线扫描算法。本质上:
因此,对于给定的任何日期范围,您可以:
这样,您就不需要维护可能
n!
大小的活动交叉集,并且可以使用单个O(n*a)
生成有效的价目表pass(a
是当前集合中活动的平均数量;如果它很小/恒定,则足够接近线性)。A simple line sweep algorithm is what you're looking for. Essentially:
So, given any date range, you can:
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 singleO(n*a)
pass (a
being the average number of activities in the current set; close enough to linear if that's small / constant).一些建议:-
从中抽象出 DateTimeRange 。在可重用的 DateTimeRange 类中完成计算重叠、交集和并集的所有工作,而不是在此处。
不要尝试通过插入、更新和删除更新两个数据结构 - 源信息和解析的日历 - 而是更新源数据,然后使用简单的扫描重新计算解析的信息正如其他人所建议的算法。如果您需要在源数据更改之间缓存已解析的日历,请执行此操作(每当源更改时清除缓存的副本并重新计算),否则每次都重新计算它,因为内存而不是 CPU 通常是当今的瓶颈,优化是您应该做的事情仅当您需要时才这样做。
Some suggestions:-
Abstract DateTimeRange out of this. Do all the work of calculating overlaps, intersections and unions in a re-useable DateTimeRange class rather than here.
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.