【问题标题】:Kotlin generate permutations (in order) of list without duplicate elementsKotlin 生成没有重复元素的列表排列(按顺序)
【发布时间】:2020-01-13 11:19:26
【问题描述】:

是否有一种简单的(甚至可能是 Kotlin 方式)来生成给定列表(包含重复元素)的所有排列,其中:

  • 保持元素的顺序
  • 删除所有重复元素
  • 包括所有元素

例如:

鉴于列表:[A, B, C, A, B, D, A],我预计会有以下结果:

[A, B, C, D], [A, C, B, D], [B, C, A, D], [C, A, B, D], [C, B, A, A], [B, C, D, A], ...(如果还有组合的话)

以下结果无效:

[A, B, C, A, D](重复 A)

[A, B, C, A](A 重复,D 缺失)

[A, C, D, B](顺序错误)

感谢您的帮助。

【问题讨论】:

  • 为什么[A, C, D, B] 被认为是错误的,但[A, C, B, D] 不是?是不是因为D, B的顺序?
  • 是的。 [A, C, B, D][A1, C, B2, D] 生成。对于[A, C, D, B],在D 之后的某个地方需要另一个B

标签: list kotlin permutation


【解决方案1】:

这是一种以某种实用风格来实现它的方法。

它首先收集一组不同值的“指令”,这些值与应保留的出现索引配对。为此,它将唯一值映射到它们的出现次数。然后它将它们折叠到所有可能的配对组合的列表中。折叠操作从一组空排列开始,然后每个唯一值将其所有可能的保留索引与现有排列集相乘。

然后我们遍历所有指令集以应用指令:从原始列表的副本中删除每个唯一值中的所有唯一值。

fun <T> getPermutationsWithDistinctValues(original: List<T>): Set<List<T>> {
    if (original.isEmpty())
        return emptySet()
    val permutationInstructions = original.toSet()
        .map { it to original.count { x -> x == it } }
        .fold(listOf(setOf<Pair<T, Int>>())) { acc, (value, valueCount) ->
            mutableListOf<Set<Pair<T, Int>>>().apply {
                for (set in acc) for (retainIndex in 0 until valueCount) add(set + (value to retainIndex))
            }
        }
    return mutableSetOf<List<T>>().also { outSet ->
        for (instructionSet in permutationInstructions) {
            outSet += original.toMutableList().apply {
                for ((value, retainIndex) in instructionSet) {
                    repeat(retainIndex) { removeAt(indexOfFirst { it == value }) }
                    repeat(count { it == value } - 1) { removeAt(indexOfLast { it == value }) }
                }
            }
        }
    }
}

我认为复杂度是 O(n*mn) 其中 n 是不同值的数量,m 是不同值的最高重复。与其他答案相同,因为最坏情况的排列数是 mn.

【讨论】:

【解决方案2】:

Code on play.kotlinlang.org

fun main() {
    val list = listOf('A', 'B', 'C', 'A', 'B', 'D', 'A')
    generatePermutations(list, ::println)
}

/**
 * Generates all permutations described in your question
 * For the sake of performance it calls [onNextPermutation] for each permutation,
 * but it uses the same list to write permutations in,
 * so if you need to use these permutations elsewhere copy its parameter by youself
 */
fun <T> generatePermutations(elementsList: List<T>, onNextPermutation: (List<T>) -> Unit) {
    if (elementsList.isEmpty()) {
        onNextPermutation(emptyList())
        return
    }
    val elementCounts = LinkedHashMap<T, Int>() // We need to remember order in which the elements were added to map
    elementsList.forEach {
        elementCounts[it] = 1 + (elementCounts[it] ?: 0) // Count our elements
    }
    val differentElements = elementCounts.keys
    
    val totalPermutationsCount = elementCounts.values.fold(1) { a, b -> a * b }
    
    // Next 3 collections are reused through generator loop for the sake of performance
    
    val takenEntryNumbers = LinkedHashMap<T, Int>() // Number of entry of each element we will take to next permutation
    differentElements.forEach { takenEntryNumbers[it] = 0 }
    
    val entriesOfElementViewed = HashMap<T, Int>() // Count of entries of each element we already viewed while iterating elementsList
    differentElements.forEach { entriesOfElementViewed[it] = 0 }
    
    val currentPermutation = ArrayList<T>() // Mutable list which we will use to write permutations in
    repeat(differentElements.size) { currentPermutation.add(elementsList[0]) } // Just fill it to needed size
    
    repeat(totalPermutationsCount) { // Generate next permutation
        var entriesTaken = 0 // Total count of entries taken in this permutation
        for (element in elementsList) { // Generate current permutation
            if (entriesOfElementViewed[element] == takenEntryNumbers[element]) {
                currentPermutation[entriesTaken++] = element
            }
            entriesOfElementViewed[element] = 1 + (entriesOfElementViewed[element] ?: 0)
        }
        onNextPermutation(currentPermutation)
        // Update collections to start next permutation
        differentElements.forEach { entriesOfElementViewed[it] = 0 }
        // Generate next permutation of entry numbers, where each entry number is less than element's total count
        for (element in differentElements) { 
            if (1 + (takenEntryNumbers[element] ?: 0) == elementCounts[element]) {
                takenEntryNumbers[element] = 0
            }
            else {
                takenEntryNumbers[element] = 1 + (takenEntryNumbers[element] ?: 0)
                break
            }
        }
        
    }
    
}

输出:

[A、B、C、D]

[B、C、A、D]

[B、C、D、A]

[A、C、B、D]

[C、A、B、D]

[C、B、D、A]

解决 O(listSize * permutationsCount) 中每个列表泛型类型的问题

【讨论】:

  • 谢谢:)。它按描述工作。 +1也适用于非递归方法。但是,我发现它(诚然正确)会生成重复项。例如输入val list = listOf('A', 'B', 'C', 'A', 'B', 'D', 'A', 'B', 'A') 生成[B, C, D, A] 两次。但我们可以在之后过滤它。
【解决方案3】:

试试下面的这个解决方案。高度递归但易于理解(最接近我们的想法)。一个缺点是内存使用。 由你来写一个非递归的。

fun <T> allPermutations(set: Set<T>): Set<List<T>> {
    if (set.isEmpty()) return emptySet()

    fun <T> _allPermutations(list: List<T>): Set<List<T>> {
        if (list.isEmpty()) return setOf(emptyList())

        val result: MutableSet<List<T>> = mutableSetOf()
        for (i in list.indices) {
            _allPermutations(list - list[i]).forEach{
                item -> result.add(item + list[i])
            }
        }
        return result
    }

    return _allPermutations(set.toList())
}

输入是一个集合。对于您的问题,您只需在调用方法之前将列表转换为集合:allPermutations (yourList.toSet())

【讨论】:

  • 效果很好:pl.kotl.in/qvNS32tei - 可能是这里最短的解决方案。
  • 这可以进一步简化为:pl.kotl.in/5vcDCrFaB
  • 以某种方式使 Array.toSet() 的编译程序崩溃,从头开始运行时也会导致 IntelliJ 崩溃
【解决方案4】:
fun <T>getUniquePermutation( array: Array< in T>): List<Pair<T,T>>{
    var permutationList = mutableListOf<Pair<T,T>>()
    for (i in array.toSet()){
        for (j in array.toSet()){
            permutationList.add((i as T to j as T))
        }
    }
   // println(permutationList)
    return permutationList
}

【讨论】:

    猜你喜欢
    • 2011-05-14
    • 2013-04-22
    • 2018-03-26
    • 2015-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-18
    • 2022-06-27
    相关资源
    最近更新 更多