我发现了类似的问题,但没有考虑优先考虑。
R : (------------|xxxxxxx|ooo|xx|----------------) (---)
S1: (------------) (----------------) (---)
S2: (xxxxxxxxxxxxxxx) (xxxxxx)
S3: (ooooooo) (oo)
假设我有3个日期范围来源,名称S1,S2和S3分别为优先级1,2和3(1为最高)和结果R.我需要结果为非重叠日期范围优先级最高的地方。
我想到了一个解决方案,但它非常顺序。首先,我创建一个按升序日期排序的表,按优先级递减(在日期冲突的情况下,最高优先级在表中首先出现),其ID和动作(开放或近距离):
ID | Action | Priority | Date |
--------------------------------
S1a | Open | 1 | 1 |
S2a | Open | 2 | 2 |
S1a | Close | 1 | 3 |
S3a | Open | 3 | 4 |
S2a | Close | 2 | 5 |
S2b | Open | 2 | 6 |
S3a | Close | 3 | 7 |
S1b | Open | 1 | 8 |
S2b | Close | 2 | 9 |
S3b | Open | 3 | 10 |
S3b | Close | 3 | 11 |
S1b | Close | 1 | 12 |
S1c...
然后我开始迭代这个表并填写有序列表和结果表:
所以第一行是:
Order List: Result:
ID | Priority | ID | Action | Date |
S1a| 1 | S1a| Open | 1 |
第二行,添加了S2a的开放日期但没有写任何东西,因为表中存在更大的优先级:
Order List: Result:
ID | Priority | ID | Action | Date |
S1a| 1 | S1a| Open | 1 |
S2a| 2 |
第三行,关闭S1a,写入结束日期,由于S2a移动到列表顶部,它也会写入S2a的开放日期。
Order List: Result:
ID | Priority | ID | Action | Date |
x S1a| 1 | S1a| Open | 1 |
S2a| 2 | S1a| Close | 3 |
S2a| Open | 3 |
我想你可以看到这是怎么回事......很多交叉检查等但是在纸上似乎有效。如果有人需要,我可以更好地解释算法,但我不认为这很难理解。如果有序列表中有更高的优先级,则不会写任何内容。删除较高优先级后,下一个最大优先级将再次打开。
也许有人有更好,更具体的想法?
谢谢你的时间!
答案 0 :(得分:1)
另一种方法是以相反的方式构建它,首先填充具有最低优先级的表,并在用较高优先级项填充日期时简单地覆盖日期。这样就不必创建订单列表并跟踪打开等待启动的项目。
注意 :我应该事先说下面的算法几乎肯定效率不高(我没有做数学而是直觉 告诉我它的效率较低)。这只是另一种接近的方式 问题
以这种方式对每个事件的开放日期和结束日期进行分组会更有益。为了简单起见,我将把开始日期称为结束日期作为“事件”。因此,您首先添加最低优先级的每个事件。然后,您开始查看按优先级组织的数据,然后按日期查看。像这样:
ID | Action | Priority | Date |
--------------------------------
S3a | Open | 3 | 4 |
S3a | Close | 3 | 7 |
S3b | Open | 3 | 10 |
S3b | Close | 3 | 11 |
S2a | Open | 2 | 2 |
S2a | Close | 2 | 5 |
S2b | Open | 2 | 6 |
S2b | Close | 2 | 9 |
S1a | Open | 1 | 1 |
S1a | Close | 1 | 3 |
S1b | Open | 1 | 8 |
S1b | Close | 1 | 12 |
-------------------------------
因此,只需执行最低优先级并将所有内容添加到结果中:
<强> RESULT 强>:
ID | Action | Priority | Date |
--------------------------------
S3a | Open | 3 | 4 |
S3a | Close | 3 | 7 |
S3b | Open | 3 | 10 |
S3b | Close | 3 | 11 |
这是事情变得有趣的地方,现在你开始查看下一个最高优先级的事件。因此,我们遇到了第一个事件S2a
,我们要做的是搜索S2a Open
和S2a Close
之间的结果中的日期。如果我们抽象地思考一下,我们会得到3种不同的情况:
在第一种情况下,由于事件的开始将被推到更高优先级事件的末尾。这是将包含的事件设置为当前更高优先级事件的Close
。
在第二种情况下,由于事件的结束是在较高优先级事件开始之后,它必须提前结束。因此,我们将包含事件的结尾设置为当前优先级较高的事件的Open
。
在最后一种情况下,整个事件包含在优先级较高的事件中,因此将被完全取消。那就是删除开头和结尾。
因此,如果我们查看您的示例,我们会S2a Open
= 2且S2a Close
= 5.该范围中包含的唯一日期是S3a Open
。因此,我们会将S3a Open
的日期更改为S2a Close
或5的值。所以现在我们的结果如下:
<强> RESULT 强>:
ID | Action | Priority | Date |
--------------------------------
S2a | Open | 2 | 2 |
S2a | Close | 2 | 5 |
S3a | Open | 3 | 5 |
S3a | Close | 3 | 7 |
S3b | Open | 3 | 10 |
S3b | Close | 3 | 11 |
从其中推断其余部分是如何形成的并不难。 (但如果您想要更多解释,请告诉我。)
根据信息的组织方式和所涉及的数据结构,这可能不如您所描述的那样有效。但我认为这一点更直观,因为您首先调度最低优先级,然后修改它们以便为更高优先级腾出时间。我没有看到你给出的解决方案有问题,它确保你只看一次每个条目(而我可以多次查看该项目,或修改最终被后来的事件破坏的项目)。 / p>
我不推荐我的解决方案超过你的解决方案,但你没有要求提高效率,只是采用不同的方式来看待它。
答案 1 :(得分:0)
我最近编写了一个执行此操作的算法。我保留两个数组:一个像你的结果和一个间隔S的数组(不是日期,而是你所谓的ID)。最初,数组S在打开'('并且结果为空。
我还保留了一种订单清单,但与你的不同之处在于它没有按优先级排序,而是关闭')'。这样可以快速从订单列表中删除不再需要考虑的时间间隔,因为它们“在过去”:您可以在索引过去之前保留索引。
然而,实际的实现并没有单独的订单列表,而是重新使用S:
对于i的S [i]&gt; index_1是打开'('
)的输入对于i&lt; i [i] index_1是在关闭')'
上排序的订单列表对于i&lt; i [i] index_2'位于过去'
因此,当创建结果条目并且我们需要获得下一个S时,只有范围index_1 - &gt;需要搜索index_2(对于具有最高优先级的区间)。
让我们看看我是否可以重新创建前几次迭代:
// initialize
S = [S1a,S2a,S3a,S2b,S1b,S3b,S1c]
R = []
index_1 = 0;
index_2 = 0;
// first result starts at smallest open
R1.start = S1a.open()
date = S1a.open() // 'current time'
// check if next entry in S ends the result R1
S[++index_1] = S2a: S2a.open() < S1a.close and S2a.Priority < S1a.Priority
// this does not end R1 -> keep S sorted, update index_2
S[index_1 - 1] = S1a, S[index_1] = S2a, S1a.close < S2a.close // -> OK! no swap needed
date = S2a.open()
S[index_2] = S1a: S1a.close() < date // -> OK! no update of index_2 needed
// check if next entry in S ends the result R1
S[++index_1] = S3a: S3.open() > S1a.close
// this ends R1 -> create it, update index_2
date = S1.close()
R1.close() = date
S[index_2] = S1a: S1a.close() = date // -> S1a is 'past'
index_2++ // update index_2
S[index_2] = S2a: S2a.close() < date // -> OK! no further update of index_2 needed
// find the next interval for R2
search S[i] index_2 <= i < index_1 and pick max priority
in this case index_2 = 1 and index_1 = 2 so we only need to check one entry (S2a)
R2.open = max( date, S2a.open() )
// continue...
抱歉任何小错误,但我希望能传达算法的想法(不打算提供所有细节)
不确定性能,它取决于数据的类型,我猜(如果你有多个区间之间的重叠,范围index_2 - index_1很大并且搜索该范围以及在关闭时对它进行排序')'每次a新的间隔添加到订单列表变得昂贵;但是,如果你在后续的时间间隔内只有重叠index_2 - index_1只有1个元素,那么前面提到的两个动作都非常便宜)
如果这是一个问题,那肯定会减少内存的成本。
答案 2 :(得分:0)
上周我写了另一个算法;改善了最坏情况的复杂性。它有一个优先级队列(我使用了堆);这最初是空的。并且在打开日期排序的一系列间隔。
A = [S1a,S2a,S3a,S2b,S1b,S3b,S1c]
H = []
我们迭代A(提前时间); A的每个元素都将排入队列。由于它是优先级队列,因此根元素H [0]包含具有最高优先级的元素。一旦关闭,根元素就会出列。
您将看到结果是优先级队列的根元素,其间隔对应于该元素成为根(最高优先级)的“时间”,直到它从队列中出队(它已关闭) )或被排队到队列中的新元素“推下”(在关闭之前被优先级更高的区间取代)。
更正式:
当我们采用A [i ++]中的下一个元素时(提前时间);我们将它与优先级队列中当前的最高元素H [0]进行比较;并检查A [i] .Open&gt; H [0] .Close。
while( A[i].Open > H[0].Close )
{
if( H[0].Close >= from ):
We have a new result; R( S = H[0]; Open = from; Closed = H[0].Closed )
Set from = H[0].Closed + 1;
Dequeue H[0]
}
一旦A [i]。打开&lt; H [0]。关闭,我们将A [i]排入优先级队列。有两件事可以发生;
A [i]是具有最高优先级的区间,并成为新的根。然后我们有了新的结果; R(S =老根H [0]; Open = from; Closed = A [i] .Open);设置自= A [i]。打开
或者A [i]“消失”到堆中,H [0]不会改变;我们继续。
一旦我们迭代了A;我们继续将队列排队,必要时创建结果;直到队列为空。
对于内存,您可以使用单个数组实现此操作,并且只在此数组中进行交换;第一个x索引是堆;最后的y个元素是数组A.
运行您的示例:
// i = 0:入队S1a
A = [S2a,S3a,S2b,S1b,S3b,S1c]
H = [S1a]
// i = 1:入队S2a
A = [S3a,S2b,S1b,S3b,S1c]
H = [S1a,S2a]
// i = 2:第一个结果(---- |;出队S1a;入队S3a
A = [S2b,S1b,S3b,S1c]
H = [S2a,S3a]
// i = 3:第二个结果| xx |;出列S2a(这使得S3a成为根);入队S2b;第三个结果| oo | (因为S2b从根位置驱动S3a)
A = [S1b,S3b,S1c]
H = [S2b,S3a]
// i = 4;排队S1b;第四个结果| xx |
A = [S3b,S1c]
H = [S1b,S3a,S2b]
// i = 5;排队S3b
A = [S1c]
H = [S1b,S3a,S2b,S3b]
// i = 6;第五个结果| ----);出列S1b,使S3a,S2b和S3b出列而不产生结果;入队S1c
A = []
H = [S1c]
//第六个结果( - );出列S1c;停止
答案 3 :(得分:0)
使用优先级队列的Bartel算法相当不错 - 算法因不相交而间断,因为:
while( A[i].Open > H[0].Close )
{
if( H[0].Close >= from ):
We have a new result; R( S = H[0]; Open = from; Closed = H[0].Closed )
Set from = H[0].Closed + 1;
Dequeue H[0]
}
非重叠区间的情况实际应该是:
while( A[i].Open > H[0].Close )
{
if( H[0].Close >= from ):
We have a new result; R( S = H[0]; Open = from; Closed = H[0].Closed )
Set from = A[i].from;
Dequeue H[0]
}
这是新的来自下一个时间间隔