递归地将一组n个对象的所有分区查找为k个非空子集

问题描述

我想将n个元素的所有分区都分为k个子集,这是我基于递归公式的算法,用于查找所有Stirling second numbers

fun main(args: Array<String>) {
    val s = mutableSetof(1,2,3,4,5)
    val partitions = 3
    val res = mutableSetof<MutableSet<MutableSet<Int>>>()
    partition(s,partitions,res)
    //println(res)
    println("Second kind stirling number ${res.size}")
}

fun partition(inputSet: MutableSet<Int>,numOfPartitions: Int,result: MutableSet<MutableSet<MutableSet<Int>>>) {
    if (inputSet.size == numOfPartitions) {
        val sets = inputSet.map { mutableSetof(it) }.toMutableSet()
        result.add(sets)
    }
    else if (numOfPartitions == 1) {
        result.add(mutableSetof(inputSet))
    }
    else {
        val popped: Int = inputSet.first().also { inputSet.remove(it) }

        val r1 = mutableSetof<MutableSet<MutableSet<Int>>>()
        partition(inputSet,numOfPartitions,r1) //add popped to each set in solution (all combinations)

        for (solution in r1) {
            for (set in solution) {
                set.add(popped)
                result.add(solution.map { it.toMutableSet() }.toMutableSet()) //deep copy
                set.remove(popped)
            }
        }
        val r2 = mutableSetof<MutableSet<MutableSet<Int>>>()
        partition(inputSet,numOfPartitions - 1,r2) //popped is single elem set

        r2.map { it.add(mutableSetof(popped)) }
        r2.map { result.add(it) }
    }
}

代码对于k = 2效果很好,但是对于更大的n和k,它会丢失一些分区,在这里我找不到错误。 示例:n = 5,k = 3输出 Second kind stirling number 19的正确输出为25。

解决方法

如果您可以阅读Python代码,请考虑下一个算法,该算法已从我对set分区的实现中迅速适应为相等大小的部分。

递归函数用N个值填充K个零件。

lastfilled参数有助于避免重复-它增加了每个零件中前导(最小)元素的顺序。

empty参数旨在避免出现空零件。

def genp(parts:list,empty,n,k,m,lastfilled):
    if m == n:
        print(parts)
        global c
        c+=1
        return
    if n - m == empty:
        start = k - empty
    else:
        start = 0
    for i in range(start,min(k,lastfilled + 2)):
        parts[i].append(m)
        if len(parts[i]) == 1:
            empty -= 1
        genp(parts,m+1,max(i,lastfilled))
        parts[i].pop()
        if len(parts[i]) == 0:
            empty += 1


def setkparts(n,k):
    parts = [[] for _ in range(k)]
    cnts = [0]*k
    genp(parts,-1)

c = 0
setkparts(5,3)
#setkparts(7,5)
print(c)

[[0,1,2],[3],[4]]
[[0,3],[2],1],[2,4],[3]]
[[0,[3,4]]
[[0,2,[1],[1,[4]]
[[0],[3]]
[[0],3,[2]]
[[0,[2]]
[[0],3]]
[[0],4]]
25
,

不确定,代码中的确切问题是什么,但是以递归的方式查找所有斯特林第二个数字要简单得多:

value="1" <?= isset($_POST["grade"]) && $_POST["grade"] == "1" ? "checked" : "" ?>
,

我改进了 Kornel_S 的代码。 有一个 func 可以列出所有可能的组合。小心大数字:)

def Stirling2Iterate(List):
    
    Result = []
    
    def genp(parts:list,lastfilled):
        
        if m == n:
            nonlocal Result
            nonlocal List
            Result += [ [[List[item2] for item2 in item] for item in parts] ]
            return
        
        if n - m == empty: start = k - empty
        else: start = 0
        
        for i in range(start,lastfilled + 2)):
            
            parts[i].append(m)
            if len(parts[i]) == 1: empty -= 1
            genp(parts,m + 1,lastfilled))
            parts[i].pop()
            if len(parts[i]) == 0: empty += 1
    
    def setkparts(n,k):
        parts = [ [] for _ in range(k) ]
        cnts = [0] * k
        genp(parts,-1)
    
    for i in range(len(List)): setkparts(len(List),i + 1)
    
    return Result

示例:

# EXAMPLE

print('\n'.join([f"{x}" for x in Stirling2Iterate(['A','B','X','Z'])]))

# OUTPUT

[['A','Z']]
[['A','X'],['Z']]
[['A','Z'],['X']]
[['A','B'],['X',['B']]
[['A',['B','X']]
[['A'],['X'],['B'],['Z']]
[['A'],['X']]
[['A'],'Z']]
[['A'],['Z']]