多组交叉操作的平均复杂性及其在引擎盖下的实现

时间:2018-03-14 10:32:57

标签: python python-3.x algorithm data-structures set

在Python wiki上没有提到multiple-sets intersection的平均情况复杂性:

https://wiki.python.org/moin/TimeComplexity

只给出了最坏情况的复杂性:

(n-1)*O(l) where l is max(len(s1),..,len(sn))

multiple-sets intersection操作的平均复杂程度是多少? 如何在引擎盖下实施此操作?

set.intersection(s1,s2,s2,s4 ...sn)

multiple-sets intersection操作是否以与two-sets intersection操作不同的方式实现,因为根据python-wiki,它们的最坏情况复杂性不同:

2集交集:O(len(s) * len(t)) 多组交集:(n-1)*O(l) where l is max(len(s1),..,len(sn))

因此,使用多集公式的两组的复杂性应该是:

--> (2-1)*O(l) where l is max(len(s1), len(s2)`
--> O(max(len(s1), len(s2))

我认为它与两组交叉操作的复杂度表示法有很大的不同。

另外,对于不同集合之间的成员资格检查,有没有比设置交集更好的方法?

注意: 我正在寻找一个解释,而不仅仅是复杂的O()表示法:)

2 个答案:

答案 0 :(得分:2)

As already answered中的{p> similar question,两套交集的实现是analogous to

def intersect(a, b):
    if len(a) > len(b):
        a, b = b, a

    c = set()
    for x in a:
        if x in b:
            c.add(x)
    return c

对于多个集合,它是implemented as a chain of pairwise intersections roughly equivalent to

def intersect_multi(a, *others):
    result = a.copy()
    for other in others:
        newresult = result.intersect(other)
        if not newresult:
            return set()
    result = newresult

可能没有给出平均复杂度,因为它取决于在通过所有others之前是否返回,因为交叉点是空的。因此,它可以是O(k)之间的任意值,kothers中第一组的长度,最差的情况。

最坏情况的复杂性是(N-1) * max(O(set_intersection))。如您所述,O(set_intersection)通常是O(min(k, l)),但如果第二个不是一组,则为O(max(k, l))。我想这包括在这里,所以它基本上由最长的集合确定。

如Raymond Hettinger所指出的on this post,维基中所述O(set_intersection)的最坏情况不太可能发生。显然,它只会在每次都有哈希冲突的情况下发生,因此if x in b变为O(n)(最坏情况下的复杂性)。

似乎这种最坏情况不包含在多组交集的最坏情况复杂性中(可能不是因为对所有集合的所有成员进行哈希冲突的可能性非常小?)。

答案 1 :(得分:2)

CPython的源代码中负责多个集合交集的此方法的基础C实现称为set_intersection_multi。这是代码:

set_intersection_multi(PySetObject *so, PyObject *args)
{
    Py_ssize_t i;
    PyObject *result = (PyObject *)so;

    if (PyTuple_GET_SIZE(args) == 0)
        return set_copy(so);

    Py_INCREF(so);
    for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
        PyObject *other = PyTuple_GET_ITEM(args, i);
        PyObject *newresult = set_intersection((PySetObject *)result, other);
        if (newresult == NULL) {
            Py_DECREF(result);
            return NULL;
        }
        Py_DECREF(result);
        result = newresult;
    }
    return result;
}

正如您所看到的那样,它循环传递给调用者的参数(python对象),并尝试计算预期的set与所有其他传递对象的交集。

Python的Wiki中提到的最坏的情况在这里是完全合理的。由于两组st之间的交集的复杂性为O(len(s) * len(t)),因此创建多组(s1&amp; s2&amp; ...&amp; sn)的最坏情况)当所有集合都有效且包含项目并且循环执行N - 1次 * 时发生。

这意味着它在所有集合之间执行n-1单个交叉点,在计算Big O表示法时,我们应该只考虑最大长度。因此,它是(n-1)*O(l) where l is max(len(s1),..,len(sn))

另外,如果你想更好地理解地球上两个集合或集合与另一个可迭代之间的交集的复杂性(因为你可以做set(x).intersection(list(y))之类的事情){{1我强烈建议您仔细查看set_intersection函数的源代码。

<子> 第一个参数在循环之前复制到O(len(s) * len(t))