繁体   English   中英

最长的唯一子序列

[英]Longest increasing unique subsequence

我有一个看起来像这样的列表/数组:

[ 0  1  2  3  4  5  6  7  3  9 10 11 13 13 14 15 16 17 18 19  4 16 22  5  3   
  2 10 17 34  5 11 18 27 14 11 15 29  2 11 10 19 32  8 27  1 32  6  2  0]

该列表应该是单调的(严格增加)。 它不是,但是您可以看到它主要在增加。 不适合此模式的值可以视为噪声,我希望将其删除。 因此,我想提取此列表的最大可能子集,这将是严格增加的数字序列。 这里有许多可能的单调序列,但重点是要找到最大的单调序列。

获取要删除的值的索引很重要,因为我需要知道其余数字的确切位置(因此,除了删除数字,我们还可以用f.ex. Nonenan-1代替它们) 。

无法更改任何数字的顺序,只需删除不适合的数字即可。

其余列表必须严格增加,因此如果我们有f.ex。 [11 13 13 14]两个 13都必须删除。

如果有多个可能的解决方案相等,则我们将无法使用其中任何一个,而必须选择数量少1个的解决方案。 F.ex. [27 29 30 34 32]我们必须丢掉34和32,因为我们不能选择一个。 如果我们有[27 29 34 15 32] ,就没有可能的解决方案,因为我们无法在[27 29][27 34][29 34][15 32]

上面列出的最佳解决方案是:

[ 0  1  2  3  4  5  6  7 -1  9 10 11 -1 -1 14 15 16 17 18 19 -1 -1 22 -1 -1   
 -1 -1 -1 -1 -1 -1 -1 27 -1 -1 -1 29 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]

谁能想到可以完成此特定工作的算法? 如果您能使我参与其中,那也将不胜感激。

到目前为止,我唯一的想法是for n in range(N, 0, -1):循环for n in range(N, 0, -1):其中N是列表的大小。 循环将首先尝试找到大小为n=N ,然后对于n=N-1n=N-2等等。当为特定n恰好找到1个解时,它将停止并返回该解。 我不确定循环中应该包含什么。

更新:

另一个SO问题提供了一种Python算法,用于查找列表的最长子序列。 这几乎是我想要做的,但不完全是。

我已经复制了该函数(请参见下文),并在末尾添加了一些额外的代码, if fullsize=True更改输出。 然后重建具有其原始形状的原始序列,但将不属于递增序列的数字替换为nans。 然后,我检查是否有多次重复出现,如果发生,将所有出现的次数替换为nans。

由于原始算法没有提供唯一的解决方案,因此仍必须对其进行更改。

例如:

a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 
    18, 19, 20, 16, 35, 35, 33, 32, 1, 35, 13, 5, 32, 8, 35, 29, 19, 
    35, 19, 28, 32, 18, 31, 13, 3, 32, 33, 35, 31, 0, 21]
print subsequence(a)

[  0.   1.   2.   3.   4.   5.   6.   7.   8.   9.  10.  11.  12.  13.  14.
  15.  16.  32.  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan
  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan  nan
  nan  nan  nan  nan]

相反结尾的.. 16 32 nan ..它应该有结束... 16 nan ... nan 31 nan nan 32 33 35 nan nan nan] ,据我所看到的。

比较简单的例子:

a = [0,1,2,3,4,1,2,3,4,5]
print subsequence(a)

[  0.   1.   2.   3.  nan  nan  nan  nan  nan   5.]

但它应该只给出[0 nan ... nan 5]因为1 2 3 4出现两次并且不是唯一的。

这里是代码的当前半工作版本(用于我的示例运行):

import numpy as np

def subsequence(seq, fullsize=True):
    """
    Credit:
    http://stackoverflow.com/questions/3992697/longest-increasing-subsequence
    """

    M = [None] * len(seq)    # offset by 1 (j -> j-1)
    P = [None] * len(seq)

    # Since we have at least one element in our list, we can start by
    # knowing that the there's at least an increasing subsequence of length one:
    # the first element.
    L = 1
    M[0] = 0

    # Looping over the sequence starting from the second element
    for i in range(1, len(seq)):
        # Binary search: we want the largest j <= L
        #  such that seq[M[j]] < seq[i] (default j = 0),
        #  hence we want the lower bound at the end of the search process.
        lower = 0
        upper = L

        # Since the binary search will not look at the upper bound value,
        # we'll have to check that manually
        if seq[M[upper-1]] < seq[i]:
            j = upper

        else:
            # actual binary search loop
            while upper - lower > 1:
                mid = (upper + lower) // 2
                if seq[M[mid-1]] < seq[i]:
                    lower = mid
                else:
                    upper = mid

            j = lower    # this will also set the default value to 0

        P[i] = M[j-1]

        if j == L or seq[i] < seq[M[j]]:
            M[j] = i
            L = max(L, j+1)

    # Building the result: [seq[M[L-1]], seq[P[M[L-1]]], seq[P[P[M[L-1]]]], ...]
    result = []
    pos = M[L-1]
    for _ in range(L):
        result.append(seq[pos])
        pos = P[pos]

    result = np.array(result[::-1])    # reversing

    if not fullsize:
        return result  # Original return from other SO question.

    # This was written by me, PaulMag:
    # Rebuild original sequence
    subseq = np.zeros(len(seq)) * np.nan
    for a in result:
        for i, b in enumerate(seq):
            if a == b:
                subseq[i] = a
            elif b > a:
                break
        if np.sum(subseq[np.where(subseq == a)].size) > 1:  # Remove duplicates.
            subseq[np.where(subseq == a)] = np.nan

    return subseq  # Alternative return made by me, PaulMag.

这是一个经典的动态编程问题。

您为每个元素存储以该元素结尾的最大序列的长度。 对于第一个元素,该值为1(仅接受该元素)。 其余的取max(1,1 +分配给其他先前元素的值<=然后是当前元素)。

您可以实现2个循环(O(N ^ 2))。 如果您的数据非常大,可能可以做一些优化。 或者知道您的序列最有用的是仅检查先前的X元素。

要修复数据,请从分配的最大值之一(最长的单调序列的长度)开始,然后用-1替换所有内容,然后在列表中向后查找序列中的前一个元素(应为< =,则当前值和所分配的值应为当前元素所分配值的-1),而您找不到匹配项,则该元素不属于该对象。 当找到匹配项时,将其作为当前匹配项并继续向后进行,直到找到分配了1的元素(这是第一个)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM