scipy 函数multiset_permutations的渐近效用迭代的时间复杂度

tquggr8v  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(113)
    • bounty将在4天后过期**。回答此问题可获得+100声望奖励。Alucard希望引起更多人关注此问题。

我想知道scipy中函数multiset_permutations的时间复杂度。
我们可以使用这个函数:

from sympy.utilities.iterables import multiset_permutations
from sympy import factorial
[''.join(i) for i in multiset_permutations('aab')]

我想知道使用这个函数的时间复杂度与工具间函数排列的时间复杂度的比较。
我已经在文档中搜索了它,但我找不到它。

4ioopgfo

4ioopgfo1#

检查sympy 1.11 multiset_permutations的源代码。
我们可以看到,如果你有所有唯一的元素,它产生的排列从itertools导入。
因此,对于这种情况,复杂性是相同的,但multiset_permutations会增加开销。
如果我们以abcdegfhij的排列为例,itertools.permutations需要约300ms,multiset_permutations需要约700ms。
然而permutations做了更多的工作,从文档中获取参考实现。

def ref_permutations(iterable, r=None):
    # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
    # permutations(range(3)) --> 012 021 102 120 201 210
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    if r > n:
        return
    indices = list(range(n))
    cycles = list(range(n, n-r, -1))
    yield tuple(pool[i] for i in indices[:r])
    while n:
        for i in reversed(range(r)):
            cycles[i] -= 1
            if cycles[i] == 0:
                indices[i:] = indices[i+1:] + indices[i:i+1]
                cycles[i] = n - i
            else:
                j = cycles[i]
                indices[i], indices[-j] = indices[-j], indices[i]
                yield tuple(pool[i] for i in indices[:r])
                break
        else:
            return

它已经比实际的itertools.permutations慢了40倍。
如果你有每个元素的频率列表,比如n = list(collections.Counter(x).values())multiset_permutations的复杂度是O(factorial(sum(n)) / prod(factorial(ni) for ni in n)),而排列是O(factorial(sum(n)),如果sum(1 for _ in itertools.product(x)) / sum(1 for _ in multiset_permutations(x)) > 100,那么multiset_permutations应该更快。
但要注意的是,它的复杂性至少是不同元素数量的阶乘。如果达到15,你就有大麻烦了。

相关问题