Простой рекурсивный алгоритм в Haskell
import Data.List
combinations 0 lst = [[]]
combinations n lst = do
(x:xs) <- tails lst
rest <- combinations (n-1) xs
return $ x : rest
Сначала мы определим особый случай, т. Е. Выберем нулевые элементы. Он выдает один результат, который является пустым списком (то есть списком, который содержит пустой список).
При n> 0 x
проходит через каждый элемент списка, а xs
- каждый элемент после x
.
rest
выбирает n - 1
элементов из xs
, используя рекурсивный вызов combinations
. Конечным результатом функции является список, в котором каждый элемент равен x : rest
(то есть список, в котором x
в качестве головы и rest
в качестве хвоста) для каждого различного значения x
и rest
.
> combinations 3 "abcde"
["abc","abd","abe","acd","ace","ade","bcd","bce","bde","cde"]
И, конечно, поскольку Haskell ленив, список постепенно создается по мере необходимости, так что вы можете частично оценить экспоненциально большие комбинации.
> let c = combinations 8 "abcdefghijklmnopqrstuvwxyz"
> take 10 c
["abcdefgh","abcdefgi","abcdefgj","abcdefgk","abcdefgl","abcdefgm","abcdefgn",
"abcdefgo","abcdefgp","abcdefgq"]