Python 如何基于第一个元素以各种方式对列表进行配对

Python 如何基于第一个元素以各种方式对列表进行配对,python,Python,我有一个特别的问题,我正试图解决,但运气不太好 这很难完全解释,但我有一个列表,我正在通过我正在编写的意图检测算法生成,我需要根据每个子列表的第一个元素将它们配对在一起 列表将按从低到高的顺序进行排序,基本上每个数字都有一个数字列表可供配对。因此,如果索引[0][0]是3,那么如果需要,可以与任何10、11、18、19或20个重复项配对。等等 SUB_NOUNS = { 3: [10, 11, 18, 19, 20], 10: [22], }

我有一个特别的问题,我正试图解决,但运气不太好

这很难完全解释,但我有一个列表,我正在通过我正在编写的意图检测算法生成,我需要根据每个子列表的第一个元素将它们配对在一起

列表将按从低到高的顺序进行排序,基本上每个数字都有一个数字列表可供配对。因此,如果索引[0][0]是3,那么如果需要,可以与任何10、11、18、19或20个重复项配对。等等

SUB_NOUNS = {
             3: [10, 11, 18, 19, 20],
             10: [22],
}
下面是一些我的输入和期望的输出的例子,解释了我需要做什么

# For only 1 item leave unchanged
In: [[3, 1, 0]]
Out: [[3, 1, 0]]

# Second item is sub item of first item - so join.
In: [[3, 1, 0], [10, 2, 0]]
Out: [[3, 1, 0, 10, 2, 0]]

# Second and third item are sub items of first item, make two lists.
In: [[3, 2, 0], [10, 3, 0], [10, 4, 0]]
Out: [[3, 2, 0, 10, 3, 0], [3, 2, 0, 10, 4, 0]]

# Three items, no sub items - unchanged
In: [[3, 1, 0], [3, 2, 0], [3, 3, 0]]
Out: [[3, 1, 0], [3, 2, 0], [3, 3, 0]]

# Third item is sub item of second item which is sub item of first item - Join all
In: [[3, 2, 0], [10, 3, 0], [22, 12, 0]]
Out: [[3, 2, 0, 10, 3, 0, 22, 12, 0]]

# Third item is sub item of second and first item, make two lists.
In: [[3, 1, 0], [3, 2, 0], [18, 0, 0]]
Out: [[3, 1, 0, 18, 0, 0], [3, 2, 0, 18, 0, 0,]]


希望我已经解释得足够好了,我并不是在寻找一个完美的解决方案,因为这里还没有列出其他可能的组合,但如果有人能给我指出正确的方向,我可能会从中找到答案。

你的问题比快速阅读看起来要困难得多

也就是说,我尝试了几件事(因为这让我很固执……),我将提出:(1)观察/问题,(2)可能的答案,(3)对您最初的解决方案的一些评论

从需求概述开始:

  • 你有一个列表
  • 子列表根据其第一个元素进行排序
  • 您有一个映射,
    SUB_nomes
    ,它根据每个列表的第一个元素告诉我们哪些列表后面可以跟着哪些其他列表
  • 您正在寻找潜在的组合
观察结果(可能还有开放性问题):

  • 在OP中,您将显示一个包含2个条目的SUB_名词示例。我假设也可能有两个以上的条目
  • [[3,2,0],[10,3,0],[22,12,0]]
    的示例中,我们看到您正在寻找可能最长的组合,因此:
    • 组合可以有2+个级别(无界)。这闻起来像是“递归”,这就是建议的解决方案
    • 虽然
      [10,3,0],[22,12,0]
      (没有
      [3,2,0]
      )是有效的组合,但您不会在示例输出中列出它。这使我假设您不想重用已在较长组合中使用的列表作为另一个组合的起点
  • [[3,1,0],[3,2,0],[18,0,0]]
    的示例中,根据所需的输出,我们可以看到相同的列表可以参与多个组合,只要“前缀”(前面的列表)不同
提议的解决办法 如前所述,这是递归的,分为两部分:

  • get_all_combos
    ,它收集所有列表的所有可能组合,作为起点。这还控制列表重用(请参见下文)
  • get_combos
    :解决方案的递归部分,它构成给定起始列表的所有组合
  • (您可以忽略或删除日志记录,但在递归中通常需要日志记录以进行调试…)

    现在,在我们看上面代码的一些输出之前,让我们快速看一下您的解决方案(内联注释):

    更紧凑的版本应为100%相同的逻辑,如下所示:

    def pair_lists_urban(data):
        pairs = []
        for noun in data:
            try:
                result = filter(lambda x: x[0] in SUB_NOUNS[noun[0]], data)
                for each in result:
                    pairs.append(noun + each)
            except KeyError:
                pass
        return pairs if pairs else data
    
    请注意,上述两个版本都有一个矛盾/问题:它们只查看两个级别的列表以形成一个组合(即N和N+1)。这将在下一节中变得明显

    测验 因此,让我们使用您提供的示例列表运行到目前为止的所有解决方案。主要剧本是:

    import itertools
    import logging as lg
    import sys
    
    
    SUB_NOUNS = {
        3: [10, 11, 18, 19, 20],
        10: [22],
    }
    
    
    # ... funcs for each solution
    
    test_lists = [
        [[3, 1, 0]],
        [[3, 1, 0], [10, 2, 0]],
        [[3, 2, 0], [10, 3, 0], [10, 4, 0]],
        [[3, 1, 0], [3, 2, 0], [3, 3, 0]],
        [[3, 2, 0], [10, 3, 0], [22, 12, 0]],
        [[3, 1, 0], [3, 2, 0], [18, 0, 0]],
    ]
    
    for l in test_lists:
        print(f"\nIn: {l}")
        print(f"Recur Out: {get_all_combos(l)}")
        print(f"Iter. Out: {pair_lists(l)}")
        print(f"Iter2 Out: {pair_lists_urban(l)}")
    
    从输出来看,我们有(附评论):

    考虑 专注于递归解决方案,我将进一步研究几个案例:

  • 如果“循环”出现在SUB_名词中怎么办
  • 如果我们有更多的SUB_名词呢
  • 在建议的解决方案中,我们如何控制列表重用
  • 为此,我使用以下测试:

    SUB_NOUNS = {
        3: [10, 11, 18, 19, 20],
        10: [22, 30],
        # Third level. Note that we can have 10->22->30 and 10->30
        22: [30, 42],
        # Unhandled case where a larger number can be followed by a smaller number
        30: [1, 22],
    }
    
    
    test_list = [[3, 2, 0], [10, 3, 0], [22, 12, 0], [30, 0, 6], [42, 8, 9]]
    
    print(f"\nIn: {test_list}")
    print(f"Recur No Reuse: {get_all_combos(test_list)}")
    print(f"Recur Reuse   : {get_all_combos(test_list, True)}")
    
    从简单的一个开始:递归解决方案总是从左到右处理列表,并且只在当前列表之后处理(
    data[list_num+1://code>),从而避免了循环。这样我们就不会返回并避免无限递归。但是,这也是需要记住的一个限制,也是上面的
    子名词[30]
    无效的原因(因为30之后输入中永远不会出现1或22)

    有了更多的次名词,你就有了更多的“分支”和潜在的组合。输出为:

    In: [[3, 2, 0], [10, 3, 0], [22, 12, 0], [30, 0, 6], [42, 8, 9]]
    Recur No Reuse: [
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [3, 2, 0, 10, 3, 0, 30, 0, 6],
    ]
    Recur Reuse   : [
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [3, 2, 0, 10, 3, 0, 30, 0, 6], 
        [10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [10, 3, 0, 30, 0, 6], 
        [22, 12, 0, 30, 0, 6], 
        [22, 12, 0, 42, 8, 9],
    ]
    
    注:开放式要点:

    • 您可以在上面的示例中看到事物如何分支,以及如何考虑10->30和10->22->30组合
    • 您还可以看到双分支:在10->22路径上,我们有10->22->30和10->22->42
    • 注意,在第二次调用中,我们在
      get\u all\u组合中设置了
      allow\u reuse=True
      。这允许代码返回其第一个元素/列表已经参与另一个组合的组合。尽管在您的
      [[3,2,0],[10,3,0],[22,12,0]]
      示例中,您似乎没有寻找这样的组合,但它们都是“正确的”,并且遵循子类名词的规则

    正如你在文章中所说的,这可能不是最好的或完美的解决方案,但我相信递归允许你做各种事情,否则在这种特殊情况下会非常困难。我发现这个问题相当复杂,因为输入数据的可能性很多,如果可能的话,对它进行一些限制并缩小范围(例如,输入总是有3个子列表),这将有助于简化解决方案。

    下面是我的解决方案,其中一个示例输入中的错误是我纠正的方法,正如@urban所指出的,这远非理想,仅仅是我为这个问题花了几个小时才想到的

        def pair_lists(self, data):
            pairs = []  # Create an empty list to store the noun pairs
            combined = False  # Variable for checking if we need to join nouns
            for noun in reversed(data):  # Loop over the nouns in reverse
                try:
                    result = itertools.filterfalse(lambda x: x[0] not in SUB_NOUNS[noun[0]],
                                                   data)  # Filter out any that aren't sub_nouns
                    for each in result:  # Loop the results
                        paired = []  # Make an empty list to pair them in to
                        if each not in paired:  # If the current iteration isn't in paired
                            paired.insert(0, each)  # insert it at index 0
                        paired.insert(0, noun)  # insert the noun at the start
                        chained = list(itertools.chain(*paired))  # chain them together
                        for pair in pairs:  # loop the pairs
                            if pair[:-3] == chained[3:]:  # if there is an intersection
                                pairs.remove(pair)  # remove the pair
                                pairs.insert(0, chained + pair[3:])  # then insert them joined
                                combined = True  # set combined to true
                        if not combined:  # if combined isn't true
                            pairs.append(chained)  # append the chained pairs
                except KeyError:
                    pass
            return pairs if pairs else data
    

    在“三项,无子项-未更改”的示例中,
    0
    在三个子列表中很常见,为什么仍然未更改?@MoinuddinQuadri重要的是
    SUB_NOUNS = {
        3: [10, 11, 18, 19, 20],
        10: [22, 30],
        # Third level. Note that we can have 10->22->30 and 10->30
        22: [30, 42],
        # Unhandled case where a larger number can be followed by a smaller number
        30: [1, 22],
    }
    
    
    test_list = [[3, 2, 0], [10, 3, 0], [22, 12, 0], [30, 0, 6], [42, 8, 9]]
    
    print(f"\nIn: {test_list}")
    print(f"Recur No Reuse: {get_all_combos(test_list)}")
    print(f"Recur Reuse   : {get_all_combos(test_list, True)}")
    
    In: [[3, 2, 0], [10, 3, 0], [22, 12, 0], [30, 0, 6], [42, 8, 9]]
    Recur No Reuse: [
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [3, 2, 0, 10, 3, 0, 30, 0, 6],
    ]
    Recur Reuse   : [
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [3, 2, 0, 10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [3, 2, 0, 10, 3, 0, 30, 0, 6], 
        [10, 3, 0, 22, 12, 0, 30, 0, 6], 
        [10, 3, 0, 22, 12, 0, 42, 8, 9], 
        [10, 3, 0, 30, 0, 6], 
        [22, 12, 0, 30, 0, 6], 
        [22, 12, 0, 42, 8, 9],
    ]
    
        def pair_lists(self, data):
            pairs = []  # Create an empty list to store the noun pairs
            combined = False  # Variable for checking if we need to join nouns
            for noun in reversed(data):  # Loop over the nouns in reverse
                try:
                    result = itertools.filterfalse(lambda x: x[0] not in SUB_NOUNS[noun[0]],
                                                   data)  # Filter out any that aren't sub_nouns
                    for each in result:  # Loop the results
                        paired = []  # Make an empty list to pair them in to
                        if each not in paired:  # If the current iteration isn't in paired
                            paired.insert(0, each)  # insert it at index 0
                        paired.insert(0, noun)  # insert the noun at the start
                        chained = list(itertools.chain(*paired))  # chain them together
                        for pair in pairs:  # loop the pairs
                            if pair[:-3] == chained[3:]:  # if there is an intersection
                                pairs.remove(pair)  # remove the pair
                                pairs.insert(0, chained + pair[3:])  # then insert them joined
                                combined = True  # set combined to true
                        if not combined:  # if combined isn't true
                            pairs.append(chained)  # append the chained pairs
                except KeyError:
                    pass
            return pairs if pairs else data