Расчески на большом наборе не вычисляют Haskell

Я пишу функцию гребенки в haskell, что ей нужно сделать, когда я предоставлю ей колоду карт, дайте мне все возможные комбинации рук из этой колоды размера x

Это соответствующий код

combs :: Int -> [a] -> [[a]]
combs 0 _      = [[ ]]
combs i (x:xs) =  (filter (isLength i) y)
            where y = subs (x:xs)
combs _ _      = [ ]

isLength :: Int -> [a] -> Bool
isLength i x
        | length x == i = True
        | otherwise     = False

subs :: [a] -> [[a]]
subs [ ] = [[ ]]
subs (x : xs) = map (x:) ys ++ ys
            where ys = subs xs

Однако, когда я прошу его вычислить гребенки 5 [1..52], например. раздача 5 из полной колоды, результата не дает, и работает очень долго
Кто-нибудь знает в чем проблема и как ускорить этот алгоритм?


person LordofTurtles    schedule 12.10.2014    source источник


Ответы (2)


Чтобы извлечь i элементы из x:xs, вы можете действовать двумя способами:

  • вы сохраняете x и извлекаете только i-1 элементов из xs
  • вы отбрасываете x и извлекаете все элементы i из xs

Следовательно, решение:

comb :: Int -> [a] -> [[a]]
comb 0 _      = [[]]  -- only the empty list has 0 elements
comb _ []     = []    -- can not extract > 0 elements from []
comb i (x:xs) = [ x:ys | ys <- comb (i-1) xs ]  -- keep x case
                ++ comb i xs                    -- discard x case

Кстати, приведенный выше код также «доказывает» известную рекурсивную формулу для биномиальных коэффициентов. Возможно, вы уже встречали эту формулу, если посещали занятия по математическому анализу. Полагая B(k,n) = length (comb k [1..n]), имеем

B(k+1,n+1) == B(k,n) + B(k+1,n)

что является прямым следствием последней строки кода выше.

person chi    schedule 12.10.2014
comment
Возникает та же проблема, что и в решении Карстена, поскольку это зарезервированное ключевое слово, и я получаю ошибку синтаксического анализа при вводе | Кроме того, я не пытаюсь извлечь элементы i из x:xs, я пытаюсь сгенерировать все возможные комбинации C(i,x:xs), например. сколько разных комбинаций из 5 я могу вытащить из колоды из 52 карт, а затем сгенерировать каждую возможную комбинацию - person LordofTurtles; 12.10.2014
comment
@LordofTurtles Разве comb 5 allCards не решит это? Следует извлечь все возможные руки, игнорируя порядок. - person chi; 12.10.2014
comment
О, подождите, ваш код делает именно то, что я имел в виду, отлично! На данный момент я не особенно хорош в понимании списков, поэтому я был сбит с толку. - person LordofTurtles; 12.10.2014

Прямо сейчас немного сложно понять, что вы пытаетесь сделать, но я думаю, что у вас есть проблемы в том, что вы будете фильтровать и отображать много.

Я думаю, что простой способ получить то, что вам нужно, это:

module Combinations where

import Data.List (delete)

combs :: Eq a => Int -> [a] -> [[a]]
combs 0 _ = [[]]
combs i xs = [ y:ys | y <- xs, ys <- combs (i-1) (delete y xs) ]

который использует delete из Data.List

Нужно быть достаточно ленивым, чтобы быстро находить вам комбинации - конечно, все это займет некоторое время ;)

λ> take 5 $ combs 5 [1..52]
[[1,2,3,4,5],[1,2,3,4,6],[1,2,3,4,7],[1,2,3,4,8],[1,2,3,4,9]]

как это работает

это один из тех рекурсивных комбинаторных алгоритмов, который работает, выбирая первую карту y из всех карт xs, а затем рекурсивно gets the rest of the handysfrom the deck without the selected carddelete xsand then putting it back togethery:ys` внутри монады списка (здесь с использованием списков).

Кстати: таких колод 311 875 200 ;)

версия без списков-включений

вот версия без понимания на случай, если у вашей системы есть проблемы здесь:

combs :: Eq a => Int -> [a] -> [[a]]
combs 0 _ = [[]]
combs i xs = do
  y <- xs
  ys <- combs (i-1) (delete y xs)
  return $ y:ys

версия, которая удалит перестановки

этот использует Ord для получения сортировки элементов в порядке возрастания и при этом удаляет дубликаты в отношении перестановки - для этого ожидается, что xs будет предварительно отсортирован!

Примечание. Версия chi работает с меньшим количеством ограничений и может быть более преформной, но я думаю, что это красиво и читабельно, и хорошо сочетается с предыдущей версией, поэтому, возможно, она вас заинтересует.

Я знаю, что это не часто делается в Haskell/FP, где вы боретесь за самые общие и абстрактные случаи, но я пришел из среды, где большинство стремится к удобочитаемости и пониманию (кодирование для программиста, а не только для компилятора) - так что будьте осторожны ;)

combs' :: Ord a => Int -> [a] -> [[a]]
combs' 0 _  = [[]]
combs' i xs = [ y:ys | y <- xs, ys <- combs' (i-1) (filter (> y) xs) ]
person Random Dev    schedule 12.10.2014
comment
Два вопроса: Поскольку as является зарезервированным ключевым словом, не должно ли оно быть y:ys? и когда я копирую ваш код, чтобы попробовать его, я получаю ошибку синтаксического анализа при вводе | - person LordofTurtles; 12.10.2014
comment
@LordofTurtles Я также добавил определение модуля - вы получите ошибки, если скопируете его в какой-нибудь файл .hs и все равно загрузите в ghci? - person Random Dev; 12.10.2014
comment
@LordofTurtles - кстати: куда ты это положил? GHCi? - или это какая-то другая система Haskell? - person Random Dev; 12.10.2014
comment
Теперь он создает ошибку синтаксического анализа в модуле ввода, как ни странно, я использую GHCi, да, возможно, связанный, выше, где я помещаю расчески, это утверждение: instance Ord Hand, где x compare y = (handCategory x) compare (handCategory y) - person LordofTurtles; 12.10.2014
comment
@LordofTurtles извините, но я думаю, что ваша установка может дать сбой - можете ли вы поместить свой код в GIST или добавить его к своему вопросу, чтобы я мог посмотреть? Может быть, у вас есть какие-то вкладки там (?) - person Random Dev; 12.10.2014
comment
Прошу прощения, я сделал ошибку и забыл закомментировать часть моего кода гребенки, что привело к ошибке синтаксического анализа. Ваш код работает нормально, за исключением того, что он выдает слишком много вывода, гребенки 4 [1,2,3,4] должны только дать [[1,2,3,4]] в качестве вывода, но он также включает в себя другие 3 перестановки этого набора, любой способ легко отфильтровать их? - person LordofTurtles; 12.10.2014
comment
вы имеете в виду перестановки [1,2,3,4]? ну да - я добавляю - но в основном это будет версия, которую тебе дал чи - person Random Dev; 12.10.2014