Вы можете сделать это следующим образом:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(emptyList())
else nonexhaustivePermutations(length - 1, components)
.flatMap { sub -> components.map { sub + it } }
Теперь я объясню, как вы можете исправить свое решение. Прежде всего, я переформатирую это:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else components.map { elm ->
nonexhaustivePermutations(length - 1, components).map { sub -> listOf(elm) + sub }
}.fold(listOf(listOf())) { result, elm -> result + elm }
Первая проблема - elm -> nonexhaustivePermutations(length - 1, components)
. Здесь вы вызываете nonexhaustivePermutations
с одинаковыми аргументами для каждого elm
на каждом шаге рекурсии. Поэтому я предлагаю заменить components.map
на nonexhaustivePermutations(length - 1, components).map
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> listOf(elm) + sub }
}.fold(listOf(listOf())) { result, elm -> result + elm }
Но в дополнение к значительному улучшению производительности этот обмен также изменяет порядок возвращаемых элементов. Это можно частично исправить, заменив listOf(elm) + sub
на sub + elm
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> sub + elm }
}.fold(listOf(listOf())) { result, elm -> result + elm }
Если вы запустите этот метод, вы увидите, что он возвращает перестановки в порядке увеличения длины. Поэтому короткие перестановки добавляются в первую очередь. Чтобы быть более точным, они добавляются во время создания списка. Таким образом, чтобы избавиться от них, fold(listOf(listOf()))
необходимо заменить на fold(emptyList())
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).map { sub ->
components.map { elm -> sub + elm }
}.fold(emptyList()) { result, elm -> result + elm }
Эта версия работает правильно. Но единственное, что делает последняя строка - это выравнивание списка. И выравнивание можно объединить с отображением, заменив map
на flatMap
:
fun <T> nonexhaustivePermutations(length: Int, components: List<T>): List<List<T>> =
if (components.isEmpty() || length <= 0) listOf(listOf())
else nonexhaustivePermutations(length - 1, components).flatMap { sub ->
components.map { elm -> sub + elm }
}