在列表列表中查找最长递增子序列的最有效方法
Most efficient way to find longest incrementing subsequence in a list of lists
我正在做一些信号分析,其中一部分是寻找最长的子序列
我有如下字典:
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 实现,或其他有效的除了从这个字典生成所有可能的序列然后 运行 耐心排序之外,还有其他方法可以做到这一点?
我会实施 "brute-force" 解决方案...
- 保留 "current sequences" 的列表,最初为空
- 对于每个键,检查当前序列中的任何一个是否可以扩展一个步骤。当增加序列更新时,也是迄今为止最好的解决方案。
- 对于未用于扩展序列的任何数字,开始一个长度为 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
将一个常量值与每个序列相关联的想法所吸引...但这不会检测到 "gaps" (即键 1 中的值 100 和键 3 中的值 102,但是 没有键2中的101)。
输入问题后的解决方案是 (7, 735, 7)
,这意味着一个 7 元素序列在键 7 处以值 735 结尾。
与@6502 的解决方案相比,这个解决方案不仅保留了最佳解决方案,而且还跟踪每个递增的子序列,如果这更有帮助的话。
这个想法类似于滑动 window 方法。您从第一个列表开始,更新 currentHotItems
和 globalHotItems
词典,然后查看第二个列表并再次更新词典,等等
# 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
是包含结果的字典。键是(值,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)
我正在做一些信号分析,其中一部分是寻找最长的子序列
我有如下字典:
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 实现,或其他有效的除了从这个字典生成所有可能的序列然后 运行 耐心排序之外,还有其他方法可以做到这一点?
我会实施 "brute-force" 解决方案...
- 保留 "current sequences" 的列表,最初为空
- 对于每个键,检查当前序列中的任何一个是否可以扩展一个步骤。当增加序列更新时,也是迄今为止最好的解决方案。
- 对于未用于扩展序列的任何数字,开始一个长度为 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
将一个常量值与每个序列相关联的想法所吸引...但这不会检测到 "gaps" (即键 1 中的值 100 和键 3 中的值 102,但是 没有键2中的101)。
输入问题后的解决方案是 (7, 735, 7)
,这意味着一个 7 元素序列在键 7 处以值 735 结尾。
与@6502 的解决方案相比,这个解决方案不仅保留了最佳解决方案,而且还跟踪每个递增的子序列,如果这更有帮助的话。
这个想法类似于滑动 window 方法。您从第一个列表开始,更新 currentHotItems
和 globalHotItems
词典,然后查看第二个列表并再次更新词典,等等
# 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
是包含结果的字典。键是(值,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)