【问题标题】:Most efficient way to find longest incrementing subsequence in a list of lists在列表列表中查找最长递增子序列的最有效方法
【发布时间】:2015-09-06 19:32:50
【问题描述】:

我在做一些信号分析,其中一部分是寻找最长的子序列

我有如下字典:

sequenceDict = {
    0: [168, 360, 470],
    1: [279, 361, 471, 633, 729, 817],
    2: [32, 168, 170, 350, 634, 730, 818],
    3: [33, 155, 171, 363, 635, 731, 765, 819],
    4: [352, 364, 732, 766, 822],
    5: [157, 173, 353, 577, 637, 733, 823, 969],
    6: [158, 174, 578, 638, 706, 734, 824],
    7: [159, 175, 579, 707, 735],
    8: [160, 464, 640, 708, 826],
    9: [173, 709, 757, 827],
    10: [174, 540, 642, 666, 710],
    11: [253, 667, 711],
    12: [254, 304, 668],
    13: [181, 255, 831],
    14: [256, 340, 646, 832],
    16: [184, 416], 
    17: [417], 
    18: [418], 
    19: [875], 
    20: [876], 
    23: [217], 
    24: [168, 218, 880], 
    25: [219, 765, 881], 
    26: [220, 766], 
    27: [221], 
    28: [768], 
    29: [3, 769], 
    30: [344, 476, 706]}

这些本质上总是另一个数组的排序索引,我想找到最长的递增序列(就像longest increasing subsequence),通过从每个键中顺序选择一个数字(键2紧跟在键1之后,所以开),例如, 从键 0 和 1 来看,[360, 361] 是一个序列,而 [470, 471] 是另一个序列。 我称这些递增序列,因为这些数字应该严格增加 1。

我看过patience sorting之类的东西,但由于这个问题略有不同,并且还有一个序列树,是否有任何已知的python实现,或者除了生成所有可能的其他有效方法来自这个 dict 的序列,然后运行耐心排序?

【问题讨论】:

  • 能否减少示例中的数据量。我们真的不需要太多来了解问题以及您期望的解决方案。你真的需要发布你尝试过的东西。
  • dict 中的每个数组似乎都已排序。这是给定的,还是只是巧合?
  • @haraldkl:这是给定的,相应地编辑了问题。
  • @PeterWood:减少了问题中的数据。正如我所提到的,我唯一的想法是蛮力方法。实现的链接已发布,唯一的其他内容是 for 循环。
  • @haraldkl 序列需要在keys的序列中“从每个key中依次选择一个数字(key 2紧跟在key 1之后,以此类推)”,如果不清楚,请提出,我会更新问题。

标签: python algorithm signal-processing


【解决方案1】:

我只会实施“蛮力”解决方案...

  1. 保留“当前序列”列表,最初为空
  2. 为每个键检查是否有任何当前序列可以扩展一步。增加序列更新也是迄今为止最好的解决方案。
  3. 对于任何未用于扩展序列的数字,开始一个长度为 1 的新序列

Python 提供了set,这可能是一个合理的选择……这是一个示例实现:

best = None
current_sequences = set()
last_key = None
for key in sorted(sequenceDict.keys()):
    data = set(sequenceDict[key])
    new_sequences = set()
    if last_key == key-1:
        # no gap in key value, may be some sequence got extended
        for val, count in current_sequences:
            if val+1 in data:
                # found a continuation, keep this sequence
                new_sequences.add((val+1, count+1))
                data.remove(val+1)
                if best is None or count+1 > best[0]:
                    # we've got a new champion
                    best = count+1, val+1, key
    # add new sequences starting here
    for v in data:
        new_sequences.add((v, 1))
        if best is None:
            best = 1, v, key
    current_sequences = new_sequences
    last_key = key

一个棘手的部分是,如果键中有间隙,则不能扩展序列,这就是 last_key 的用途。

复杂度应该是O(input_size × average_number_of_sequences)。我只是一种直觉,但我的猜测是你不能低于这个。我很想使用value - key 将一个常数值与每个序列相关联...但是这不会检测到“间隙”(即键 1 中的值 100 和键 3 中的值 102,但 without 键 2 中的 101)。

输入问题后,解决方案是(7, 735, 7),意思是一个 7 元素序列,在键 7 处以值 735 结尾。

【讨论】:

  • 谢谢,这太好了,+1 因为心灵感应知道可能有空键,这实际上是一个真实的场景。但是,如果可以看到相互竞争的解决方案,那就太好了。你能改变你对未来读者的答案吗?
  • @SahilM:您可以在每次主循环迭代时打印current_sequences(即每个键一次)。这是一组(current_value, length) 整数对...
【解决方案2】:

与@6502 的解决方案相比,这个解决方案不仅保持最佳解决方案,而且还跟踪每个递增的子序列,如果这更有帮助的话。

这个想法类似于滑动窗口的方法。您从第一个列表开始,更新 currentHotItemsglobalHotItems 字典,然后查看第二个列表并再次更新字典,等等。

# fill missing indexes in the dictionary:
for i in range(min(sequenceDict), max(sequenceDict)):
    if i not in sequenceDict:
        sequenceDict[i] = []

# get only lists, ordered:
sortedItems = map(lambda x:x[1], sorted(sequenceDict.items(), key=lambda x:x[0]))    
globalHotItems = {} # (value, startIndex): length
currentHotItems = {} # value: length

for i in range(len(sortedItems)):
    updatedHotItems = {} # updated value: length
    for item in sortedItems[i]:
        if (item - 1) in currentHotItems:
            updatedHotItems[item] = currentHotItems[item-1] + 1
        else:
            updatedHotItems[item] = 1

    deadSet = set(currentHotItems.keys()) - \
            set(updatedHotItems.keys() + [key - 1 for key in updatedHotItems.keys()])

    for item in deadSet:
        globalHotItems[ (item-currentHotItems[item]+1, i-currentHotItems[item]) ] = currentHotItems[item]

    currentHotItems = updatedHotItems

print sorted(globalHotItems.items(), key=lambda x:x[1])[-1]

globalHotItems 是包含结果的字典。键是 (value, startIndex),值是长度。

例如globalHotItems中的最后4项:

print sorted(globalHotItems.items(), key=lambda x:x[1])[-4:]

是:

[((157, 5), 4), ((217, 23), 5), ((706, 6), 6), ((729, 1), 7)]

这意味着最好的解决方案是长度为 7 并且从 index=1 列表中以 729 开始。最好的第二个解决方案是长度为 6 并从 index=6 列表中以 706 开始,等等。

复杂性:

我认为复杂性应该再次出现:O(input_size × average_number_of_sequences)

【讨论】:

  • +1 用于竞争解决方案的附加功能。但是,您可以重新检查您的代码吗?当我用整个数据集运行它时,值和长度是正确的,但索引不正确。
  • @SahilM,问题是由于给定字典中缺少索引。一开始我不知道您缺少索引...但是,我修复了它并现在更新了我的答案,它应该可以工作
  • 如果你能提供一个这个解决方案失败的具体例子,我可能会尽力帮助你。
猜你喜欢
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 2012-03-22
  • 2013-07-03
  • 1970-01-01
相关资源
最近更新 更多