【问题标题】:Smallest list containing all elements from two lists, while preserving order包含两个列表中所有元素的最小列表,同时保留顺序
【发布时间】:2014-06-03 07:33:31
【问题描述】:

我不确定如何组合两个整数列表中的项目,以便保留项目的顺序,并且结果列表(如果连接成一个整数)尽可能小。

可能类似于这个问题,虽然给出的答案没有解决我的大小限制: Interleave different length lists, elimating duplicates and preserve order in Python

例如,给定:

a = [3,4,5,7,9,2]
b = [3,5,7,4,2,8]

这两个列表的最短可能组合是:

c = [3,4,5,7,4,9,2,8]

连接整数值为 34574928

在某些情况下,数字的顺序不会影响列表长度,但会影响连接整数的大小。在给出的示例中,可以交换 4 和 9,同时仍保持项目的顺序,但最终的数字会比需要的大。

进一步说明:

最终列表必须包含两个原始列表中每个数字的每个实例。为了更好地表示上例中两者的结合:

a = [3,4,5,7,  9,2  ]
b = [3,  5,7,4,  2,8]
c = [3,4,5,7,4,9,2,8]

当然,它不会总是那么干净。在这种情况下,两个列表(3、5、7 和 2)中的四个数字可以完全合并。如果不创建更大的列表,则无法组合其中的四个数字(4、4、9 和 8)。例如:

a =     [3,    4,5,7,  9,2]
b =     [3,5,7,4,  2,8    ]
bad_c = [3,5,7,4,9,2,8,9,2]

在这种情况下,我只组合了 3 和 4 之一。当连接这两个示例结果中的项目时,我们得到:

c =     34574928
bad_c = 357492892

它们都满足排序要求,但是因为有一个不同的结果满足排序要求但在连接成整数时小于bad_c,所以bad_c是不正确的结果。

【问题讨论】:

  • 不清楚你的列表组合规则是什么。你想达到什么目的?
  • 我正在寻找包含来自其他两个任意整数的所有数字的最小可能整数。数字必须保持顺序,但不一定需要连续。即,给定 13 和 12,满足规则的最小数字将是 123。
  • 所以,你要去 [a1,b1,a2,b2...]。并且您需要确定交织的顺序,以使最终的整数尽可能小。当你交织时,第一个位置是否决定了列表其余部分的编织?或者任何位置都可以先从任一列表中获取值?
  • 那么,鉴于列表[1,2,3,2][4,2,6,1]:我可以这样做吗?:[1,4,2,3,6,1,2]?还是我需要做[1,4,2,3,6,2,1],因为我在第一个位置选择了第一个列表?
  • 最重要的是要记住结果数字的大小。在这种情况下,我认为(但不确定)[1,4,2,3,1,6,2] 会满足规则。这是因为整数 1423162 包含两个列表中的所有数字,同时保留两个列表中数字的顺序,并最小化它自己的大小(与其他可能保留列表顺序的答案相比,但导致一个更大的最终整数值。即 42621231)。我希望这是有道理的......

标签: python list sequence-alignment branch-and-bound


【解决方案1】:

这是一个相当长但正确的(据我从问题讨论中得知)使用递归的实现。

要点:

  • 我使用.pop(index) 遍历这两个列表。这让我可以使用递归,因为随着函数的递归,两个列表都变得越来越小,导致一个列表是len(0)
  • 可以从任一列表中选择数字,并且可以从单个列表中连续选择的数字没有限制
  • 不允许连续重复。
  • 比较两个不相等的数字时,较小的数字总是排在较大的位置。 23xxx 始终低于 32xxx。

基本上,如果我有 [1,2,3][6,0,4] 之类的东西,第一个列表中的所有数字将在第二个列表中的第一个数字之前,因为 1236xx 将 始终小于 6xxxxx、1236xx 小于 16xxxx 和 1236xx 小于 126xxx,无论为 x 选择的值如何。

z = [None]
#set to None so that z[-1] doesn't throw an out-of-range error

def small_list(a,b): #recursive function

    ### BASE CASE ###

    if len(a) == 0: #if one list empty, can only add rest of other list
        for i in b:
            if i != z[-1]: #account for duplicates
                z.append(i)
            # else: #don't add duplicates

        return z.pop(0) #end recursion, remove extraneous None

    elif len(b) == 0: #if one list empty, can only add rest of other list
        for j in a:
            if j != z[-1]:#account for duplicates
                z.append(j)
            # else: #don't add duplicates

        return z.pop(0) #end recursion, remove extraneous None

    #Otherwise, we need to check whichever is smaller.  
    #The smaller number should ALWAYS go in the larger place (tens,hundreds,etc.) to make a smaller number.

    ### RECURSIVE CASE ###

    if a[0] < b[0]:
        if a[0] != z[-1]:
            z.append(a.pop(0))
        else:
            a.pop(0)
    elif a[0] > b[0]:
        if b[0] != z[-1]:
            z.append(b.pop(0))
        else:
            b.pop(0)
    elif a[0] == b[0]:
        a.pop(0)

    small_list(a,b) # recur

例子:

z = [None]

l1 = [1,2,3,2]
l2 = [2,1,1,1]

small_list(l1,l2)
print z

第一个示例打印[1, 2, 1, 3, 2]现在正确。

z = [None]

l1 = [1,2,3]
l2 = [4,5,6]

small_list(l1,l2)
print z

第二个示例打印[1, 2, 3, 4, 5, 6],这也是现在正确的。

这是它如何计算您上面给出的示例的流程:

# The format is: [final list]  len(a)  [list a]  len(b)  [list b]

[] len(a) = 6 [3, 4, 5, 7, 9, 2] len(b) = 6 [3, 5, 7, 4, 2, 8]
# 3 repeated, so remove it.
[] len(a) = 5 [4, 5, 7, 9, 2] len(b) = 6 [3, 5, 7, 4, 2, 8]
# add lower of first two indices to final (4 v 3), and remove from corresponding list
[3] len(a) = 5 [4, 5, 7, 9, 2] len(b) = 5 [5, 7, 4, 2, 8]
# add lower of first two indices to final (4 v 5), and remove from corresponding list
[3, 4] len(a) = 4 [5, 7, 9, 2] len(b) = 5 [5, 7, 4, 2, 8]
# 5 repeated, so remove it.
[3, 4] len(a) = 3 [7, 9, 2] len(b) = 5 [5, 7, 4, 2, 8]
# add lower of first two indices to final (7 v 5), and remove from corresponding list
[3, 4, 5] len(a) = 3 [7, 9, 2] len(b) = 4 [7, 4, 2, 8]
# 7 repeated, so remove it.
[3, 4, 5] len(a) = 2 [9, 2] len(b) = 4 [7, 4, 2, 8]
# add lower of first two indices to final (9 v 7), and remove from corresponding list
[3, 4, 5, 7] len(a) = 2 [9, 2] len(b) = 3 [4, 2, 8]
# add lower of first two indices to final (9 v 4), and remove from corresponding list
[3, 4, 5, 7, 4] len(a) = 2 [9, 2] len(b) = 2 [2, 8]
# add lower of first two indices to final (9 v 2), and remove from corresponding list
[3, 4, 5, 7, 4, 2] len(a) = 2 [9, 2] len(b) = 1 [8]
# add lower of first two indices to final (9 v 8), and remove from corresponding list
[3, 4, 5, 7, 4, 2, 8] len(a) = 2 [9, 2] len(b) = 0 []
# list b is empty, add first element of list a (if non-duplicate)
[3, 4, 5, 7, 4, 2, 8, 9] len(a) = 1 [2] len(b) = 0 []
# list b is empty, add first element of list a (if non-duplicate)

#Finally:
[3, 4, 5, 7, 4, 2, 8, 9, 2]

【讨论】:

  • 如果递归令人困惑,我可以使用while 循环重写。
  • 嗯...有些地方不太对劲。当我将最初问题中的两个列表(a = [3,4,5,7,9,2]b = [3,5,7,4,2,8])提供给它时​​,此函数提供了 [3,4,5,7,4,2,8,9],这是不正确的,因为列表 a 中的 9 后跟 2,而组合列表以 9 结尾,不满足规则。我想我可能解释得不好(相信我,我试图理解!),因为你的第一个例子也不符合规则。 l2 有三个 1,这意味着结果列表也需要至少三个 1。结果必须包含每个数字的所有实例。
  • 我希望这不仅仅是进一步混淆......在可能的情况下,可以在两个原始列表之间共享数字。我会尝试在我原来的问题中格式化一个更好的例子......
  • @The_Unobsequious 我想我已经修好了。这是我的循环的逻辑错误。我想我理解了你的问题,但我会重新检查。
  • @The_Unobsequious .....射击。我阅读了您发布的问题更新,这不是我认为您要问的(没有意识到 length 是一个问题)。现在这更像是一个对齐问题(因此更加困难),但绝对仍然可行。我目前的答案很好地近似于你的理想情况,但它不会总是给出最好的情况。
【解决方案2】:

这是一个我认为可行的简单算法,实现相当简单,所以我不发布它。

1.对于列表,找到第一个共同的元素,收集之前的元素说两个列表是:a & b

2.一个。如果没有共同的元素,则通过比较第一个元素然后增加较小列表的索引并进行比较来合并它们。你明白了!

2. 乙。那么不失一般性,如果我们说a[3]匹配b[4],然后将a[0]-a[2]和b[0]-b[3]一起收集,然后使用case合并这7个item其中它们没有任何共同元素,并在末尾附加 a[3] 的值。

3. 类似地这样做直到列表的末尾。在最后一个附加项之后,合并其余项。

4. 为此,我们可以编写一个函数,从两个列表中获取要合并的子列表的开始和结束索引。

我希望这个解决方案有效,它看起来是正确的,但我还没有尝试过。如果我遗漏了什么,请指出来。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-02
    • 1970-01-01
    • 2011-11-05
    • 2019-08-16
    • 1970-01-01
    相关资源
    最近更新 更多