【问题标题】:Find a specific sequence of values in one list within a larger list在较大列表中的一个列表中查找特定的值序列
【发布时间】:2019-07-19 16:59:15
【问题描述】:

我正在尝试以最有效的方式确定List1=[0,0,0] 是否包含在List2List3 中,以及在哪里:

List2=[34, 32, 25, 0, 0, 0, 32] 结果

List3=[34, 32, 25, 0, 32, 0, 0] 结果错误

我试过set().subset,但它返回TrueTrue,我试过if List1 in List2并得到False

我知道我可以遍历列表并进行值、序列比较,但想知道是否已经有一个函数可以进行这种比较,如果没有,是否可以使用相当简单的 lambda 表达式来完成?

注意:List2List3 可能会更长,这些只是显示差异的简短示例,更准确地说是我在寻找什么

【问题讨论】:

  • 如果您知道您的列表很小,您可以执行this 之类的操作,然后检查是否List1 in allSubArrays(List2)。但通用解决方案可能更好。
  • Checking if list is a sublist 问题中选择的答案将表明匹配类型与我正在寻找的匹配类型不同,以及更多答案即使是同一种搜索,也根本没有效率——所以它可能在 2 个标准级别上失败,至少在 1 个标准级别上失败——所以不是那个问题的重复

标签: python list


【解决方案1】:

我不知道为此设计的功能。但是,您可以使用 lambda 实现此目的。

is_contiguous_subsequence = lambda small, big: any(small == big[i:i+len(small)] for i in range(len(big) - len(small) + 1))

对于我的口味而言,lambda 函数中包含的字符太多,因此我建议将其设为常规函数。

def is_contiguous_subsequence(small, big):
    return any(small == big[i:i+len(small)] for i in range(len(big) - len(small) + 1))

根据any 的性质,这将在找到第一个匹配项时返回True,并且不会继续通过big 列表的其余部分。只是一点效率奖励。

【讨论】:

  • 这可能是小型搜索的最佳选择。如果您真的在寻找效率,您可以使用更复杂的功能来解决这个问题。本质上,检查每个len(small) 字符并查看它是否在set(small) 中:如果不是,则子字符串不能在跳过的部分中,将搜索长度除以len(small)。您还可以针对big 中的每个可能有效的i 仅检查序列的第一个字符,尽管python 可能已经在后台进行了这些比较,因此可能无法提供它在其他语言中的加速。
  • 感谢查看 lambda 和内联函数后,我决定将其作为最好的函数调用,因为它可以包含中断,但使用上述基本逻辑如下:def IsSubSet(self, ShrtList, LongList): RetVal = False for idx in range ( len(LongList) - len(ShrtList) ): if LongList[idx:idx+len(ShrtList)] == ShrtList: RetVal = True break return RetVal
  • @DennisJensen 您确实需要在range 内的数组长度差异上加上 +1。否则,ShrtList = [0, 0, 0]LongList = [1, 0, 0, 0] 之类的案例将失败,因为不会检查 LongList 中最后三个项目的序列。另外,我要在答案中传达的最后一点是any 将在找到的第一个匹配项上停止;这就像你的函数中的中断,只是内置的。
  • 虽然这在我的例子中是正确的,但我的列表应该是最短的 5,但感谢您的提醒
【解决方案2】:

正如一些 cmets 所指出的,可读性和效率之间存在矛盾。该解决方案提供了较大列表中较小列表的索引,可以通过检查索引是否为None来解决您的问题。

对于较小的父列表(长度为 6),以下算法比更直接的解决方案快约 2 倍,但对于较长的列表(长度为 10,000),可以快约 15 倍。

诀窍是在每个项目上使用内置的list.index() 函数来快速跳过父列表。如果我们注意到索引之间有任何大于 1 的间隙,我们就知道序列被破坏了,但是我们可以从这个点附近开始,无论它在哪里。

def index_of(parent_list, sub_list):
    # No match possible
    if len(sub_list) > len(parent_list):
        return

    # Empty list 'matches' at index 0
    if not sub_list:
        return 0

    sequence_start = 0
    while True:
        try:
            match_found, offset = _sub_match(
                parent_list, sub_list, sequence_start)
        except ValueError:
            return

        if match_found:
            return sequence_start

        sequence_start = offset


def _sub_match(parent_list, sub_list, start_at):
    pos, last_offset = 0, start_at - 1
    # Skip through the items looking for the next index after the one before

    for item in sub_list:
        offset = parent_list.index(item, last_offset + 1)

        # We jumped more than one value, so the sequence is broken
        if offset - last_offset != 1:
            return False, offset - pos

        pos += 1
        last_offset = offset

    return True, last_offset

【讨论】:

  • 这可能是一个改进,尽管值得小心的是,一些数据会退化为基本情况:[0,0,0,1] in [0,0,0,0,0,0,0,0,0,0,0,0] 将给我们提供与简单蛮力方法基本相同的运行。
  • 这是一个公平的观点。最坏情况下的性能仍然很差,但是对于随机数,平均情况要好得多。第一个项目键出现的频率越低,它就越快。我想知道您是否可以对每个部分使用相同的索引技巧?这意味着我们会直接跳到最后?
  • 在上述情况下(您可能期望从二进制传感器获得的那种),了解您的数据并为该数据编写专业算法将是您最好的选择。如果您知道数据大部分为 0,那么搜索字符串的其他部分会更有意义。如果您有一堆要比较的子字符串,您甚至可以预处理较大的字符串以提取此类信息:父字符串主要包含 0(不要使用它们进行搜索),没有 2(任何带有 2 的子字符串都可以被忽略) 等。
  • @TemporalWolf 可能是真的。感谢您指出最坏的情况。我已经用不受此影响的版本更新了该方法。我假设这是一次检查,并且预处理不是一个好的交易。我还假设算法用于一般用途(以匹配问题的标题)
【解决方案3】:

首先感谢@brentertainer 和@jon-betts 的见解。现在重申一下我需要知道的是,如果 SubList 仍然包含在 FullList 中,我看到了@jon-betts 发布的效率增强,但实现如下:

class ClassContainer:
    # This handles everything pertinent to this Class
    def __init__(self):
        self.ClassName = 'ThisClass'

    @staticmethod
    def IsSubSet(SubList, FullList):
        RetVal = False
        Item = SubList[0]
        Range = len(FullList) - len(SubList)
        LenAdjtr = len(SubList)

        for idx in range ( Range ):
            idx = FullList.index(Item, idx)
            if idx > Range:
                break
            if FullList[idx:(idx + LenAdjtr)] == SubList:
                RetVal = True
                break

        return RetVal

对于更长的序列,这无​​疑大大简化了函数,这将在此应用程序中派上用场,但不关心通过 SubList 进行挑剔,只是直接比较其全部值,这似乎更有效.

【讨论】:

  • 根据此函数接受的参数(特别是self),您似乎正在将此函数作为实例方法来实现。如果是这种情况,删除self 作为参数并用@staticmethod 装饰是有意义的,因为函数体中没有使用该实例。否则,只需删除 self 作为参数。
  • 好的@brentertainer 我把类包装器放在它周围,这样你就可以看到为什么它是用“self”方面声明的——它不是一个独立的方法,而是一个类中的方法—— - 没有“self”python 抱怨它在课堂上——也许我需要一个与“self”不同的值,但错误消息说要添加“self”,这就停止了抱怨
  • Nvm 我深入研究了你所说的并发现了你的意思 - 所以我已经添加了 - 谢谢你的提醒
  • 我可能会说得更好。但是,是的,您的最后一次编辑解决了我想说的。谢谢!
  • 不,谢谢——因为我学到了 Python 的另一个方面,我不知道但在我的脑海里想知道但没有花时间去寻找答案
【解决方案4】:

这里是函数式编程方式:

List1 = [0, 0, 0]

List2 = [34, 32, 25, 0, 0, 0]

f = lambda *args: True if \
    list(filter(lambda i: i in args[1] and \
    args[0].count(i) <= args[1].count(i), args[0])) == args[0] else False

print(f(List1, List2))
# True

【讨论】:

    【解决方案5】:

    我喜欢这种方式,定义一个返回生成器的方法:

    def each_cons(ary, n = 2):
      if n < 2: n = 1
      i, size = 0, len(ary)
      while i < size-n+1:
        yield ary[i:i+n]
        i += 1
    

    那么你可以这样使用:

    list2= = [34, 32, 25, 0,  0, 0, 32]
    chk = [0,0,0]
    
    chk in each_cons(list2, len(chk))
    #=> True
    


    自我解释:
    res = each_cons(list2, 3)
    print(list(res))
    #=> [[34, 32, 25], [32, 25, 0], [25, 0, 0], [0, 0, 0], [0, 0, 32]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-29
      相关资源
      最近更新 更多