Ruby 我如何安排泳道
我正在尝试将10000个活动安排到不同的车道上。每个事件都有开始和结束日期。车道上没有任何事件可以重叠Ruby 我如何安排泳道,ruby,database,multithreading,algorithm,Ruby,Database,Multithreading,Algorithm,我正在尝试将10000个活动安排到不同的车道上。每个事件都有开始和结束日期。车道上没有任何事件可以重叠 ====================================================================== Lane 1 [Event1] [Event4 ] [Event7 ] ===================================================================
======================================================================
Lane 1 [Event1] [Event4 ] [Event7 ]
======================================================================
======================================================================
Lane 2 [Event2] [Event 5] [Event 8]
======================================================================
======================================================================
Lane 3 [Event3 ] [Event6]
======================================================================
========time along x axis >>>>>>>>>>>>>>>>>>>>>>>>>>
因此,我的问题是如何有效地为活动确定合适的车道。我从数据库中按开始时间对事件进行排序。我采取的第一种方法是为每条车道设定最后的结束时间。对于每个新的活动,我检查每个车道,如果活动的开始时间早于车道的最后一个结束时间,我向下移动并检查下一个车道。如果我找不到一条适合我的车道,我会创建一条新的车道
class LaneManager
def initialize
@lanes = []
end
# Find the free lane given start and end of an event
def nextFreeLane start_date, end_date
@lanes.each_with_index do |lane, index|
if start_date > lane.last_date
lane.last_date = end_date
return index
end
end
lane = Lane.new
lane.last_date = end_date
@lanes << lane
@lanes.length - 1
end
end
class Lane
attr_accessor :last_date
end
类管理员
def初始化
@车道=[]
结束
#找到给定活动开始和结束的自由车道
def NEXTFLEERANE开始日期、结束日期
@车道。每个带有_索引的|车道,索引|
如果开始日期>车道最后日期
lane.last_date=结束日期
回报指数
结束
结束
车道=车道。新建
lane.last_date=结束日期
@lanes首先,将开始和结束时间一起放入一组,并按时间排序。对于每个结束,找到最近的下一个开始,并链接到它。完成后,解开线程:取第一个开始,并将其与链接到它的整个事件链一起从包中移除(请记住,每个结束都链接到最近的开始)。重复此过程,直到捆绑包中没有事件开始(因此,没有事件)为止。我将扩展我的评论问题作为答案,因为示例数据太长了
样本数据为:
create table events (
id serial primary key,
startend tsrange not null
);
insert into events (startend)
select tsrange(now()::timestamp(0) + i * interval '1 minute', now()::timestamp(0) + i * interval '1 minute 30 second' + (i % 6) * interval '10 second')
from generate_series(1,100) i;
像这样的简单联接:
with events as (
select evt.id as evt_id,
evt.startend as evt_startend,
chk.id as chk_id,
chk.startend as chk_startend
from events evt
join events chk on evt.startend && chk.startend
)
select *
from events
order by evt_startend, chk_startend
limit 10;
产生类似以下集合的结果:
evt_id | evt_startend | chk_id | chk_startend
--------+-----------------------------------------------+--------+-----------------------------------------------
1 | ["2013-06-24 15:03:27","2013-06-24 15:04:07") | 1 | ["2013-06-24 15:03:27","2013-06-24 15:04:07")
2 | ["2013-06-24 15:04:27","2013-06-24 15:05:47") | 2 | ["2013-06-24 15:04:27","2013-06-24 15:05:47")
2 | ["2013-06-24 15:04:27","2013-06-24 15:05:47") | 3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27")
3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27") | 2 | ["2013-06-24 15:04:27","2013-06-24 15:05:47")
3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27") | 3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27")
3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27") | 4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07")
4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07") | 3 | ["2013-06-24 15:05:27","2013-06-24 15:07:27")
4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07") | 4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07")
4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07") | 5 | ["2013-06-24 15:07:27","2013-06-24 15:10:47")
4 | ["2013-06-24 15:06:27","2013-06-24 15:09:07") | 6 | ["2013-06-24 15:08:27","2013-06-24 15:11:27")
(10 rows)
正如您在上面所看到的,对于任何给定的事件,您可以很容易地确定并发事件的潜在数量(给定事件右侧的列),从而确定给定事件的潜在车道数。但是,它只是一个范围:对于右侧的任何给定行id,有多种可能在左侧播种车道编号
带有一些窗口函数的有点难看的查询可能会让您确定何时需要重置车道号。问题是,重置为什么
了解后者的唯一方法是确定一个完全没有事件的时间点(在这种情况下,lane=1)。对于任何其他时间点,您所能知道的最好情况是有一条空闲车道,或者您需要创建一条新车道
class LaneManager
def initialize
@lanes = []
end
# Find the free lane given start and end of an event
def nextFreeLane start_date, end_date
@lanes.each_with_index do |lane, index|
if start_date > lane.last_date
lane.last_date = end_date
return index
end
end
lane = Lane.new
lane.last_date = end_date
@lanes << lane
@lanes.length - 1
end
end
class Lane
attr_accessor :last_date
end
因此,假设这是您的一个选项,我建议您实际存储车道号,并使用触发器自动生成它。在触发器中,对并发事件的简单查询会生成一个繁忙车道列表,允许您安全地选择或创建一个不繁忙的车道,或者如果在该时间点没有分配车道,则选择或创建车道1
然后,您的模式将更像这样:
create table events (
id serial primary key,
startend tsrange not null,
lane int not null
);
水平和垂直滚动的查询变得很简单。这被称为区间分割或区间图着色。例如,见。您的方法与最佳算法几乎相同,只是需要从正确的车道数开始,而不是根据需要添加车道数。要有效地实施,请保留一组当前空闲车道(例如,作为一个平衡的搜索树),并在间隔开始或结束时进行更新。您对5000个事件的说明不是Schlemiel的问题,而只是一个糟糕的输入案例,可能是非常不可能的输入案例。@BorisStitnicky我当前的数据集给了我这个问题。也就是说我应该删除引用。它只是转移了人们对主要问题的注意力。它仍然效率低下。我能做一些快速排序算法吗?你的问题基本上可以通过贪婪的方法解决。唯一需要注意的是,如果你在同一时间段内遇到了许多事件,你会说什么。是可以边走边存储车道,还是规格不合理?@Denis我想我无法边走边计算车道。。我获取按开始时间排序的事件。由于我不希望向用户发送所有10k事件(只是降低UX速度),因此我希望确定适合观看区域的内容-特别是他可以看到的5条车道和他选择的时间窗口。有趣的方法。将尝试更新。谢谢。如果它足够有效,考虑将逻辑移动到存储过程。谢谢@Denis。我已经完成了一个基于通道分类集的初始实现(按上次事件、结束时间和位置分类)。我会看看结果如何。性能可以接受。不过,我们不得不在Ruby中处理SortedSet的一个糟糕的默认实现。将更新。非常感谢您抽出时间。