【问题标题】:Collecting subsequences from a sequence从序列中收集子序列
【发布时间】:2018-10-19 19:35:29
【问题描述】:

假设我们有以下序列:

[1, 2, 1, 1]

我们想根据以下规则计算这个给定序列的所有子序列:

if s_i <= s_i+1 then s_i+1 is part of a subsequence with s_i

通过从序列的第一个元素(此处为 1)开始计算子序列,并将其与它的右邻居(此处为 2)进行比较。如果它们适用于条件,则形成子序列。之后,2 必须与它的右邻居1 进行比较,如果它们适用,它就会加入子序列。在这里他们没有,所以它不加入。

此过程以2 和前一个邻居的邻居1 继续,直到序列结束。之后,以类似方式继续处理2 的邻居。

下图展示了序列中第一个元素1的子序列构建过程:

因此,问题本质上是递归的。代码如下:

def calc(seq):
    for i in range(0, len(seq)):
          calc_subseq(i, seq)

def calc_subseq(i, seq):
       a = seq[i]
       for j in range(i+1, len(seq):
           b[j] = seq[j]
           if b <= a:
               calc_subseq(j, seq);
           else:
                #build subsequence
        #build subsequnce

现在的问题是:

计算后如何检索子序列?我使用了一个堆栈,并在每次调用时都传递了它。此外,我还传递了一个计数器,该计数器随着每次匹配而增加,并随着每个函数调用传递,然后也返回。如果不匹配,我会从堆栈中弹出与计数器计数一样多的项目。当在calc_subseq(seq) 中到达 for 循环的结尾时,我也会这样做。但我正在寻找更好的解决方案。

有人知道解决类似问题的算法吗?如果有更有效的方法会很有趣。我想到了某种动态编程。

编辑:

根据要求,这里是输入序列[1,2,1,1]的所有结果:

1 (0), 2 (1)
2 (1)
2 (1)
2 (1) -> end
1 (0), 1 (2), 1 (3) 
1 (3) -> end
1 (2) -> end 
1 (0), 1(3)
1 (3) -> end
1 (0) -> end
2 (1)
2 (1)
2 (1) -> end
1 (2), 1 (3)
1 (3) -> end
1 (2) -> end
1 (3) -> end

注意:索引以(x) 提供。 -&gt; end 表示已到达第二个 for 循环的末尾。因此,它显示了由于没有邻居剩下而无法比较的最后一个元素。

【问题讨论】:

  • 您所描述的内容听起来像是在寻找“单调序列”。 This question 对此有一些想法
  • 那么[1, 2, 1, 1] 的结果应该是怎样的呢?我必须说我不确定我是否理解你所描述的算法。另外,您能否添加更多示例以及您期望的结果? (像 2 或 3 就足够了)。
  • 当然,我已经为序列中的第一个元素 (1) 添加了结果。
  • 我不明白你是如何得到[1, 1, 1] 子序列的——正如我理解的解释,我以为你只会得到[1], [1, 2], [2], [1], [1, 1]..
  • 我猜我的解释很复杂。我觉得很难用语言来解释。我稍后会改变它,可能会添加一张图片,这样更容易。这里的问题是,每个元素都与它的所有邻居进行比较。如果发生匹配,则过程继续匹配元素。这就是我需要递归的原因。

标签: python algorithm sequence


【解决方案1】:

有一个大问题。如果原始序列的长度为n,则最长的上升子序列的预期长度为O(sqrt(n)),并且该序列的每个子集都是另一个上升子序列,因此至少有O(exp(sqrt(n)))。如果n 大小适中,那么此类子序列的数量很快就会变得非常非常大。

因此,我将向您展示如何创建一个紧凑的树状结构,您可以从中计算上升子序列的计数,以便您可以在有限时间内轻松生成每个答案。我没有跟踪索引,但是如果您需要,该功能将很容易添加:

def rising_tree (seq):
    tree = {}
    for item in reversed(seq):
        this_count = 1 # For the subsequence of just this item
        this_next = {}
        for next_item, data in tree.items():
            if item <= next_item:
                this_count = this_count + data[0]
                this_next[next_item] = data
        tree[item] = [this_count, this_next]
    total_count = 0
    for _, data in tree.items():
        total_count = total_count + data[0]
    return [total_count, tree]

当应用于[1, 2, 1, 1] 的示例时,您将获得以下数据结构:

[   5, # How many rising subsequences were found
    {   1: [   4, # How many start with 1
               {   1: [   2,  # How many start with 1, 1
                          {   1: [   1, # How many start with 1, 1, 1
                                     {   }]}],
                   2: [   1, # How many start with 1, 2
                          {   }]}],
        2: [   1, # How many start with 2
           {   }]}]

现在我们可以将它们全部提取出来:

def tree_sequence_iter (tree):
    items = sorted(tree[1].keys())
    for item in items:
        yield [item]
        subtree = tree[1][item]
        if (subtree[1]):
            for subseq in tree_sequence_iter(subtree):
                yield [item] + subseq


for ss in tree_sequence_iter(rising_tree([1, 2, 1, 1])):
    print(ss)

请注意,我不需要调用sorted,因为我在那里滑过,但这样我们不仅得到了唯一的子序列,我们实际上是按照字典顺序得到的! (但请记住,它们可能有很多。)

如果你真的不想要生成器(并且认为我们有内存来存储它们),我们可以简单地list(tree_sequence_iter(rising_tree(seq))) 来生成我们的列表。

【讨论】:

  • 非常感谢!请让我考虑一下。
猜你喜欢
  • 2020-01-05
  • 1970-01-01
  • 2015-10-20
  • 2022-08-14
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
  • 2018-05-19
  • 1970-01-01
相关资源
最近更新 更多