Для перестановок небольших списков я использую следующий код:
let distrib e L =
let rec aux pre post =
seq {
match post with
| [] -> yield (L @ [e])
| h::t -> yield (List.rev pre @ [e] @ post)
yield! aux (h::pre) t
}
aux [] L
let rec perms = function
| [] -> Seq.singleton []
| h::t -> Seq.collect (distrib h) (perms t)
Работает следующим образом: функция «распределить» распределяет данный элемент по всем позициям в списке, например:
distrib 10 [1;2;3] --> [[10;1;2;3];[1;10;2;3];[1;2;10;3];[1;2;3;10]]
Функция perms работает (рекурсивно) следующим образом: распределить заголовок списка по всем перестановкам его хвоста.
Функция распределения будет работать медленно для больших списков, поскольку она часто использует оператор @, но для списков разумной длины (
Одно предупреждение: если ваш список содержит дубликаты, результат будет содержать идентичные перестановки. Например:
perms [1;1;3] = [[1;1;3]; [1;1;3]; [1;3;1]; [1;3;1]; [3;1;1]; [3;1;1]]
Приятной особенностью этого кода является то, что он возвращает последовательность перестановок, а не генерирует их все сразу.
Конечно, генерация перестановок с помощью императивного алгоритма на основе массива будет (намного) быстрее, но в большинстве случаев этот алгоритм хорошо мне помог.