It's possible to improve the subsequencesOfSize
function, that user5402 mentioned. Compare this and this. This is because of (l-n)
in the second version, so subsequencesOfSize 3 [1..350]
is equal to subsequencesBySize [1..350] !! 347
, so a lot of unused lists are created.
When filtering subsequences by a predicate p
which has the property that p xs
is true implies all p (inits xs)
is true, it is possible to integrate the predicate into the generation of subsequences for even greater efficiency. Such is the case for the predicate "not containing 2 and 3".
And here is what you want:
zapWith f xs [] = xs
zapWith f (x:xs) (y:ys) = f x y : zapWith f xs ys
filterCombs :: ([a] -> Bool) -> Int -> [a] -> [[a]]
filterCombs p n xs | n > length xs = []
filterCombs p n xs = go xs id !! n where
go [] ds = [[[]]]
go (x:xs) ds
| p (ds' []) = zapWith (++) ([] : map (map (x:)) with) without
| otherwise = without
where
ds' = ds . (x:)
with = go xs ds'
without = go xs ds
zapWith
is intentionally non-exhaustive. ds
in the go
function is a difference list, that contains all previous elements. You can read go
like this: if a property p
holds for <all previous elements of xs> ++ [x]
then include combinations with x
and without x
, otherwise include only without x
.
Some examples:
conseq (x:y:xs) = succ x == y && conseq (y:xs)
conseq xs = True
main = do
print $ filterCombs (\xs -> any (`notElem` xs) [2,3]) 3 [1..5]
print $ filterCombs conseq 4 $ [1..8] ++ [8,7..1]
print $ filterCombs (all (<= 10)) 9 [1..5000]
prints
[[1,2,4],[1,2,5],[1,3,4],[1,3,5],[1,4,5],[2,4,5],[3,4,5]]
[[1,2,3,4],[1,2,3,4],[2,3,4,5],[2,3,4,5],[3,4,5,6],[3,4,5,6],[4,5,6,7],[4,5,6,7],[5,6,7,8],[5,6,7,8]]
[[1,2,3,4,5,6,7,8,9],[1,2,3,4,5,6,7,8,10],[1,2,3,4,5,6,7,9,10],[1,2,3,4,5,6,8,9,10],[1,2,3,4,5,7,8,9,10],[1,2,3,4,6,7,8,9,10],[1,2,3,5,6,7,8,9,10],[1,2,4,5,6,7,8,9,10],[1,3,4,5,6,7,8,9,10],[2,3,4,5,6,7,8,9,10]]