簡體   English   中英

滿足Haskell條件的所有大小為N的子集

[英]All the subsets of size N which satisfy a condition in Haskell

我想編寫一個函數,該函數接受一個列表並返回滿足給定條件的所有可能子集的列表。 例如,對於我想要具有[1,2,3,4]的所有3個大小的子集,但沒有包含2和3的任何子集。

$> func [1,2,3,4] 3
[[1,2,4],[1,3,4]]

要生成大小為NI的所有子集,請使用以下功能(在此處找到):

kCombinations :: [a] -> Integer -> [[a]]
kCombinations _      0 = [[]]
kCombinations []     _ =  []
kCombinations (x:xs) k = withHead ++ withoutHead
  where withHead    = map (x:) (kCombinations xs (k-1))
        withoutHead = kCombinations xs k

我知道最簡單的解決方案是先生成所有組合,然后再使用過濾器功能。 但是對於更大的問題,例如kCombinations [1..30] 6則要花很多時間才能完成。

您能告訴我在生成所有組合時如何過濾掉一些數據嗎?

這個問題很多,在這里可以找到各種方法的最新比較: 生成組合的技術比較

由於帶有備注,提到的最后一個方法( subsequencesOfSize )非常高效。 我的機器上用ghci進行的時間比較:

length $ kCombinations [1..30] 6        - time: 2.73 secs
length $ subsequencesOfSize 6 [1..30]   - time: 0.40 secs

為了解決您的原始問題(沒有2和3的子集),基本上有兩種方法可以計算答案:

  -- import Data.List

  answer1 = filter (\s -> not (elem 2 s && elem 3 s)) $ subsequencesOfSize 6 [1..30]
  answer2 = map (2:) subs23 ++ map (3:) subs23 ++ subsequencesOfSize 6 nums'
    where nums = [1..30]
          nums' = [1..30] \\ [2,3]
          subs23 = subsequencesOfSize 5 nums'

我的盒子上的時間(再次是ghci):

length answer1           -- 1.48 secs
length answer2           -- 0.36 secs

answer1顯然是幼稚的方法; answer2應用了一些基本的集合論,可以輕松地泛化不包含任何兩個數字的子集的計數-您可以決定它是否合法。

可以改善user5402提到的subsequencesOfSize函數。 比較這個這個 這是因為第二個版本中的(ln) ,所以subsequencesOfSize 3 [1..350]等於subsequencesBySize [1..350] !! 347 subsequencesBySize [1..350] !! 347 ,因此創建了許多未使用的列表。

當通過具有p xs為true的性質的謂詞p過濾子序列時,意味着all p (inits xs)都是真實的,可以將謂詞整合到子序列的生成中,以實現更高的效率。 謂詞“不包含2和3”就是這種情況。

這是您想要的:

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故意並非詳盡無遺。 go函數中的ds是一個差異列表,其中包含所有先前的元素。 可以讀取go這樣的:如果一個屬性p保持用於<all previous elements of xs> ++ [x]然后包括與組合x和無x ,否則僅包括無x

一些例子:

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]

版畫

[[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]]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM