python - 是否有一种快速算法可以将集合的所有分区生成为大小为 2 的子集(以及一个大小为 1 的子集)?

标签 python list set python-itertools combinatorics

如标题中所述,我试图生成一组大小为 n 的所有分区,其中所有子集的大小均为 2,如果 n 不均匀,则存在新的单例集。我稍微修改了一些用于生成所有分区的 SO 代码来得到这个:

def partitionIntoPairs(collection):
    if len(collection) == 1:
        yield [ collection ]
        return

    first = collection[0]
    for smaller in partition2(collection[1:]):
        for n, subset in enumerate(smaller):
            if len(subset):
                yield smaller[:n] + [[ first ] + subset]  + smaller[n+1:]
        yield [ [ first ] ] + smaller

这可行,但遗憾的是速度太慢了。我的第二个想法是使用 itertools.combinations 生成某个集合的所有对,然后递归地为每个 det 调用该函数,而不删除给定的对,但我猜这甚至更慢。另外,实现不正确,它只返回一个可能的分区,我不确定如何让它返回所有分区:

from itertools import combinations

def partitionIntoPairs2(collection):
    if not collection:
        return []
    elif len(collection) == 1:
        return [(next(iter(collection)))]
    else: 
        pairs = set(combinations(collection, 2))
        for pair in pairs: 
            collection.remove(pair[0])
            collection.remove(pair[1])
            return partition3(collection) + [pair]

我偶然发现了一些用于具有给定数量的集合的分区的算法,以及生成所有可能的分区的算法的各种实现,但据我所知,这些都没有有效地解决我的问题。

因此,提出一个更具体的问题:如果第二种算法是可行的选择,那么正确的实现是什么?当然,有没有更快的方法来做到这一点?如果是,怎么办?

最佳答案

分区应被视为一组,仅顺序不同的两个分区应被视为同一分区。所以数字集只有3个分区(1,2,3,4)。

分区的数量应该是N!/(N/2)!/2^(N/2)。使用斯特林公式,大约为。 Sqrt(2)*(N/e)^(N/2) 其中 e=2.71828...而且非常巨大。

我利用了@VirtualScooter的代码并提供了分区的递归版本,它比他的itertools版本运行得更快(注意这不是苹果与苹果的比较,因为我的分区没有重复)。


import itertools
import timeit
t3 = (1, 2, 3)
t4 = (1, 2, 3, 4)
t6 = (1, 2, 3, 4, 5, 6)

def grouper(iterable, n, fillvalue=None):
    """Collect data into fixed-length chunks or blocks.
        Code from Python itertools page
    """
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.zip_longest(*args, fillvalue=fillvalue)

def partitionIntoPairs(collection):
    perms = itertools.permutations(collection)
    for p in perms:
        group = list(grouper(p, 2))
        if group[-1][-1] is None:
            group[-1] = (group[-1][0],)
        yield group

def Partition(Indexable):
    if len(Indexable)<=2:
        yield [Indexable]
    elif len(Indexable)%2==1:
        for i,x in enumerate(Indexable):
            for s in Partition(Indexable[:i]+Indexable[i+1:]):
                yield [[x]]+s
    else:
        for i,x in enumerate(Indexable):
            if i==0:
                x0=x
            else:
                for s in Partition(Indexable[1:i]+Indexable[i+1:]):
                    yield [[x0,x]]+s
def comp_timeit(collection, repeats=1_000):
    s1 = f"l1 = list(Partition({collection}))"
    s2 = f"l1 = list(partitionIntoPairs({collection}))"
    t1 = timeit.timeit(s1, globals=globals(),number=repeats)
    t2 = timeit.timeit(s2, globals=globals(),number=repeats)
    print(f"partition, {repeats:_} runs: {t1:,.4f}")
    print(f"itertools, {repeats:_} runs: {t2:,.4f}")
for p in Partition(t4):
    print(p)
comp_timeit(t3)
comp_timeit(t4)
comp_timeit(t6)

关于python - 是否有一种快速算法可以将集合的所有分区生成为大小为 2 的子集(以及一个大小为 1 的子集)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66969865/

相关文章:

c# - 像C#中的hashSet一样模拟List,这样List就不会有重复的值

java - 从 Int[] 数组中删除重复项

javascript - 如何可靠地检查对象是 EcmaScript 6 Map/Set?

python - selenium可以在outlook online中find_element_by_xpath吗?

Java 8 - 获取列表元素的 'parent' 对象

python - 删除重音和特殊字符

c# - 在 C# 中获取列表中重复项的索引

node.js - NodeJS 如何将对象添加到集合中并进行迭代?

python - 如何测试多个变量与单个值的相等性?

python - 嵌套 FOR 循环索引问题 - Python