【发布时间】:2017-10-24 15:23:36
【问题描述】:
我目前遇到了一个算法问题,我想优化复杂性。
我有两个区间列表 S = [[s1, s2], [s3, s4], ..., [sn-1, sn]] 和 W = [[w1, w2], [w3, w4], ..., [wm-1, wm]] 我想按照序数顺序合并它们,S 的区间优先于 W 的区间。 (S为强,W为弱)
例如,该优先级意味着:
-
S = [[5,8]]和W = [[1, 5], [7, 10]]将给出:res = [[1, 4, W], [5, 8, S], [9, 10, W]]。这里从 W 开始的区间被优先裁剪为 S 的区间 -
S = [[5, 8]]和W = [[2, 10]]将给出:res = [[2, 4, W], [5, 8, S], [9, 10, W]]。这里 W 的区间被分成两部分,因为 S 具有优先级。
在合并这些列表时,我需要通过在每个间隔旁边写第三个元素来跟踪这些间隔的强弱特性,我们可以称之为符号。这就是为什么结果类似于:[[1, 4, W], [5, 8, S], [9, 10, W]]。
最后,由于所有区间的并集并没有覆盖一定范围内的所有整数,所以我们有第三个符号,假设 B 代表空白,填补缺失的区间:[[1, 2, W], [5, 8, S], [9, 10, W], [16, 20, S]] 将被填充为:[1, 2, W], [3, 4, B], [5, 8, S], [9, 10, W], [11, 15, B], [16, 20, S]]
我的第一次尝试非常天真和懒惰(因为我首先想让它工作):
如果这两个区间列表覆盖的最大整数是 M,那么我创建了一个大小为 M 的列表,其中填充了 B 符号:res = [B]*M = [B, B, B ..., B]
然后我首先从 W 开始一个接一个地取区间,并从这个区间的 index 的 res 中重写元素,以将其符号更改为 W。接下来,我对 S 的区间做同样的事情,并尊重优先级,因为我用符号 S 覆盖在最后一步。
它给出了类似的东西:
[B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B][B, B, B, W, W, W, W, B, W, W, W, W, B, W, W, B, B][B, B, S, S, W, W, W, B, S, S, W, W, B, S, W, B, B]
最后,我最后一次通过大列表来分解和重新创建间隔及其对应的符号。上一个示例给出:
[[1, 2, B], [3, 4, S], [5, 7, W], [8, 8, B], [9, 10, S], [11, 12, W], [13, 13, B], [14, 14, S], [15, 15, W], [16, 17, B]]
不幸但可以预见的是,该算法在实践中不可用:M 在我的应用程序中约为 1000000,如果我不是,该算法为 O(n2)搞错了。
所以,我想要一些建议和指导来解决这个算法复杂性问题。我确信这个问题看起来像一个众所周知的算法问题,但我不知道该去哪里。
我目前改进的一些想法可用于优化算法,但实现起来相当复杂,所以我认为有更好的想法。但他们在这里:
- 执行相同类型的覆盖过程以尊重优先级:在列表 W 中,在必要时插入 S 的间隔并覆盖以尊重优先级。然后填写此列表以插入带有 B 符号的缺失区间。但是由于大量的案例,我们会大量使用 if 来比较区间。
- 在逐步浏览 S 和 W 的同时构造一个新列表。在这个想法中,我们将一个列表一个光标从一个间隔移动到另一个间隔,直到两个列表之一的末尾。我们再次使用了很多 if 并且我们在新列表中插入关于优先级的间隔。但它会在大量案例中引发同样复杂的问题。
我希望我说清楚了,否则我可以用其他方式解释。 请用经验和智慧教我:)
谢谢
编辑:这是我的“天真”算法代码:
def f(W, S, size):
#We first write one symbol per sample
int_result = ['B'] * size
for interval in W:
for i in range(interval[0], interval[1]+1):
int_result[i] = 'W'
for interval in S:
for i in range(interval[0], interval[1]+1):
int_result[i] = 'S'
#we then factorize: we store one symbol for an interval of the same symbol.
symbols_intervals = []
sym = int_result[0]
start = 0
for j in range(len(int_result)):
if int_result[j] != sym:
symbols_intervals.append([start, j-1, sym])
sym = all_symbols[j]
start = j
if j == len(int_result)-1:
symbols_intervals.append([start, j-1, sym])
return symbols_intervals
【问题讨论】:
-
您的算法是 O(M),而不是 O(M^2)。它应该适用于 M = 1,000,000。你试过了吗?你能告诉我们你的代码吗?
-
O(n^2) 怎么样?你真的在使用大小为 M 的 list 吗?如果是这样,您可以将其替换为 array,这将为您提供更好的时间复杂度:O(M+n+m)。但是你实际上可以用你最后建议的两种方法中的任何一种来做 O(n+m) - 如果你的 M 很大,那就更好了。这确实需要许多 if 语句,但它仍然应该是可管理的!
-
我的错,当你纠正亚历克斯时,这是 O(M)。它适用于 M = 1,000,000,但它使算法非常慢。在编辑的第一篇文章中查看我的代码;)