对于集合S和T,为什么Python的S - = T取O(len(T))而不是O(len(S))?

时间:2015-03-11 10:32:23

标签: python big-o python-internals

this Python time complexity table中的设置条目,以及下面的评论确认,S.difference_update(T)需要时间O(len(T))而S - T需要O(len(S))。给出的理由是第一个的算法是"对于T中的每个元素将其从S"中移除,而第二个的算法是"对于S中的每个元素将其添加到新的集合中,如果不是在T"。

算法"对于S中的每个元素,如果它在T"中,则从S中删除它。工作得相同,是O(len(S))?为什么不做哪个更短?

我想我没有看到什么。

1 个答案:

答案 0 :(得分:3)

从技术上讲,操作中并不需要S大于T. T很可能实际上比S大得多:

>>> S = {1, 2, 3}
>>> T = {3, 4, 5, 6, 7, 8, 9}
>>> S - T
{1, 2}

因此,为所有操作选择一个或另一个算法将是一个任意选择,因为你根本不知道哪个实际上更短(如果你不知道这些集合)。

但总的来说,这并不重要。 S和T都是输入,O(|T|)O(|S|)仍然是O(n),即线性。所以这无论如何都不是问题。


我已与the source核对,以进一步确认究竟发生了什么。

  • S.difference(T)S - Tset_difference):这会计算两个设置对象之间的差异。它遍历S中的元素并检查每个元素是否包含在T中。如果不是,则将其添加到结果集中。

    如果S远大于T,则实施实际上会复制S并执行S' -= T。由于这会在S中留下许多项目,因此比从空集开始便宜并继续从S添加元素。

  • S.difference_update(T)set_difference_update):首先,它接受多个参数。所以从技术上讲,它不能检查T的长度和交换只是因为周围有多个Ts。更重要的是,它支持不自行设置的Ts(任何可迭代的),因此它只能通过迭代这些迭代并从集合中删除这些项来工作。

    所以为此,迭代S实际上是不可能的(因为我们在Ts中没有常数成员检查)。

事实证明,这就是为什么会发生这种情况的原因。这些原因大多隐藏在实际的set方法中,而不是操作符实现(虽然内部使用了这些方法)。虽然您可以进一步微观优化一些特殊情况,但如上所述,虽然从技术上讲,您仍然会留下O(n),但这并没有给您带来太多改进。在通常的应用程序中(特别是在Python中),这样的操作不太可能成为你的瓶颈。