【问题标题】:Longest increasing unique subsequence最长递增唯一子序列
【发布时间】:2016-02-20 02:16:50
【问题描述】:

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

[ 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]

这个列表应该是单调的(严格递增)。 不是,但您可以看到它大部分在增加。 不符合这种模式的值可以被认为是噪声, 我希望他们删除。 所以我想提取这个列表的最大可能子集,这将 是一个严格递增的数字序列。 这里有很多可能的单调序列, 但关键是要找到尽可能大的那个。

获取要删除的值的索引很重要, 因为我需要知道剩余数字的确切位置 (所以我们可以用替换它们而不是删除数字 前任Nonenan-1)。

我可以改变任何数字的顺序, 删除不适合的即可。

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

如果有几个同样大的可能解决方案, 我们不能使用其中任何一个,必须选择一个数字少 1 个的解决方案。 前任在[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): 其中N 是列表的大小。 循环将首先尝试找到大小为n=N 的解决方案, 然后是n=N-1n=N-2 等。 当它为特定 n 找到恰好 1 个解决方案时,它会停止并 返回该解决方案。我还不确定循环中应该包含什么。

更新:

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

我已经复制了那个函数(见下文)并在最后添加了一些额外的代码 更改了输出if fullsize=True。 然后重建具有原始形状的原始序列, 但是不属于递增序列的数字被替换 由南斯。然后我检查是否有任何数字出现不止一次, 如果是这样,用 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.

【问题讨论】:

  • 写一些代码,然后我们就可以交谈了。
  • 为什么需要替换数字?为什么不直接删除它们?
  • @PeterWood 谢谢,另一个问题中的 Python 算法可以满足我的需要,但它不会拒绝非唯一的解决方案。也许我可以修改它来做到这一点。我正在两个都在增加的序列之间进行交叉匹配。该列表是第一个列表指向第二个序列中等效对象的指针(第一次猜测),因此不应更改列表的长度。但是,如果我至少可以得到子序列,那么重新找到原始列表的相应索引以便我可以重建它会很简单,所以这并不重要。

标签: python arrays algorithm list sorting


【解决方案1】:

这是一个经典的动态规划问题。

您为每个元素存储在该元素处结束的最大序列的长度。 对于第一个元素,值为 1(只取那个元素)。对于其余部分,您取 max(1, 1 + 分配给其他先前元素的值,即

您可以使用 2 个循环 (O(N^2)) 来实现。如果您的数据非常大,您可能可以进行一些优化。或者知道你的序列大多是好的,只检查前面的 X 元素。

要修复您的数据,您从分配的最大值之一(即最长单调序列的长度)开始,将之后的所有内容替换为 -1,然后在列表中向后查找序列中的前一个元素(应该是

【讨论】:

    猜你喜欢
    • 2013-07-03
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 2018-11-01
    • 2011-09-02
    相关资源
    最近更新 更多