【问题标题】:Ordered subsets test有序子集测试
【发布时间】:2012-08-05 22:06:02
【问题描述】:

我想测试一个有序集是否是更大有序集的子集。我使用了元组和itertools.combinations:

def subset_test(a, b):
    return a in itertools.combinations(b, len(a))

例如,

>>> subset_test((0, 1, 2), (0, 3, 1, 4, 2))
True
>>> subset_test((0, 1, 2), (0, 3, 2, 4, 1))
False

它可以工作,但是当我测试大元组时速度很慢。

【问题讨论】:

  • 是的,@jamylak。我更新了问题。
  • @senderle,你能推荐一个更好的词吗?我想测试 A 是否是有序集 B 的子集。
  • 关于您的原始代码,无需调用list,您也可以在生成器中检查成员资格,它将通过组合节省一次运行。
  • 谢谢,@jamylak。我再次更新了问题。

标签: python tuples subset itertools


【解决方案1】:

您可以简单地使用迭代器来跟踪 B 中的位置

>>> A = (0, 1, 2)
>>> B = (0, 3, 1, 4, 2)
>>> b_iter = iter(B)
>>> all(a in b_iter for a in A)
True

【讨论】:

  • 哦,哇!我明白为什么会这样,但它看起来像黑魔法。 in 在这种情况下的行为是否得到保证?
  • 标准中是否有任何内容要求in 使用线性迭代并在找到匹配项时停止?我无法想象它会以任何其他方式完成,但对于“一般情况”来说可能仍然是一个问题。
  • @MadPhysicist,当然不是。 in 调用底层的 __contains__ 方法。例如,对于dictin 肯定不是线性搜索。另一方面,它必须是对迭代器的线性搜索——它怎么可能意味着别的?
【解决方案2】:

简单的方法

>>> a = (0, 1, 2)
>>> b = (0, 3, 1, 4, 2)
>>> filter(set(a).__contains__, b) == a
True

为了提高效率,请使用itertools

>>> from itertools import ifilter, imap
>>> from operator import eq
>>> all(imap(eq, ifilter(set(a).__contains__, b), a))
True

【讨论】:

  • filter(functools.partial(operator.contains, a), b)
  • @gnibbler 你是说这样比较好还是在这种情况下可以使用特殊方法?
  • 特殊方法版本的运行速度似乎快了两倍。列表理解似乎是最慢的。
【解决方案3】:

这应该让你开始

>>> A = (0, 1, 2)
>>> B = (0, 3, 1, 4, 2)
>>> b_idxs = {v:k for k,v in enumerate(B)}
>>> idxs = [b_idxs[i] for i in A]
>>> idxs == sorted(idxs)
True

如果列表推导抛出KeyError,那么显然答案也是False

【讨论】:

    【解决方案4】:

    这是一种不需要任何散列的线性时间方法(在最长的集合中)。它利用了这样一个事实,因为这两个集合都是有序的,所以不需要重新检查集合中较早的项目:

    >>> def subset_test(a, b):
    ...     b = iter(b)
    ...     try:
    ...         for i in a:
    ...             j = b.next()
    ...             while j != i:
    ...                 j = b.next()
    ...     except StopIteration:
    ...         return False
    ...     return True
    ... 
    

    一些测试:

    >>> subset_test((0, 1, 2), (0, 3, 1, 4, 2))
    True
    >>> subset_test((0, 2, 1), (0, 3, 1, 4, 2))
    False
    >>> subset_test((0, 1, 5), (0, 3, 1, 4, 2))
    False
    >>> subset_test((0, 1, 4), (0, 3, 1, 4, 2))
    True
    

    我很确定这是对的 - 如果您发现任何问题,请告诉我。

    【讨论】:

      【解决方案5】:

      这应该很快,但我有一个更快的想法,我希望尽快完成:

      def is_sorted_subset(A, B):
          try:
            subset = [B.index(a) for a in A]
            return subset == sorted(subset)
          except ValueError:
            return False
      

      更新:这是我承诺的更快的。

      def is_sorted_subset(A, B):
        max_idx = -1
        try:
          for val in A:
            idx = B[max_idx + 1:].index(val)
            if max(idx, max_idx) == max_idx:
              return False
            max_idx = idx
        except ValueError:
          return False
        return True
      

      【讨论】:

      • 请注意,如果 B 很大,因为 list.index 是 O(N),这可能会变慢
      【解决方案6】:

      这个呢?

      >>> a = (0, 1, 2)
      >>> b = (0, 3, 1, 4, 2)
      >>> set(a).issubset(set(b))
      True
      

      在此示例中,a 和 b 具有有序且唯一的元素,它检查 a 是否是 b 的子集。这是你想要的吗?

      编辑:

      根据@Marcos da Silva Sampaio 的说法:“我想测试 A 是否是有序集 B 的子集。”

      不会是这样的:

      >>> a = (2, 0, 1)
      >>> b = (0, 3, 1, 4, 2)
      >>> set(b).issuperset(a)
      True  
      

      在这种情况下,a 的顺序无关紧要。

      【讨论】:

      • a 的元素可能没有正确排序,例如。 (2, 1, 0) in (0, 3, 1, 4, 2)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多