簡體   English   中英

Haskell,算法所有可能的編號組成

[英]Haskell, algorithm all possible composition of number

我在haskell中有一個代碼,它生成數字的三部分組合:

kompozycje n = [ (x,y,z) | x<-[1..n], y<-[1..n], z<-[1..n], x+y+z==n]

我想制作類似kompozycje nk的東西,它會生成我的k-part組合,然后如果例如k等於4則會有四個變量和四個數字返回,並且在條件下會有類似u + x + y + z的東西==ñ。 有一些簡單的解決方案嗎?

是的,是的。 它使用列表monad和replicateM

import Control.Monad

summy :: Integer -> Integer -> [[Integer]]
summy k n = do
  ls <- replicateM k [1..n]
  guard (sum ls == n)
  return ls

要不就

summy k n = filter ((==n) . sum) $ replicateM k [1..n]

在列表monad中, replicateM將生成由數字1 .. n組成的所有可能的長度為k列表。

這會產生重復,如[1, 2, 1][1, 1, 2] 但你的原始方法也是如此。

碰巧的是,有一個可愛,高效且模糊(?)的算法,用於枚舉nk -size分區,其歷史可以追溯到至少1779年.Donald Knuth - 還有誰? - 在算法H下的計算機編程藝術草案中詳細描述。 在這里您可以選擇Haskell中的算法:

import Data.List (unfoldr)

partitions :: Int -> Int -> [[Int]]
partitions k n | k < 1 || k > n = []
partitions k n = initPartition : unfoldr (fmap (\x -> (x, x)) . nextPartition) initPartition
  where
    initPartition = (n-k+1) : replicate (k-1) 1

nextPartition :: [Int] -> Maybe [Int]
nextPartition [] = error "nextPartition should never be passed an empty list"
nextPartition [x] = Nothing
nextPartition (x:y:rest)
    | x-y > 1 = Just $ (x-1) : (y+1) : rest
    | otherwise = do
        (s, c, xs) <- go (x+y-1) rest
        Just $ (s-c) : c : xs
  where
    go _ [] = Nothing
    go s (z:zs)
        | x-z > 1 = let z' = z+1 in Just (s, z', z' : zs)
        | otherwise = do
            (s', c, zs') <- go (s+z) zs
            Just (s'-c, c, c:zs')

這真的是對@Aaron Roth的答案的評論,這很好(並且比接受的答案更有效)。

我認為你可以改進這個,fmap似乎沒必要。 此外,Knuth對H5 / H6的介紹(你的'go'步驟)模糊了它只是一個總和和復制。 這是一個貼近Knuth命名的版本,同時試圖讓算法更清晰:

import Data.List (unfoldr)

partitions m n
  | n < m || n < 1 || m < 1 = []
  | otherwise = unfoldr nextPartition ((n - m + 1) : (replicate (m - 1) 1))

nextPartition [] = Nothing
nextPartition [a] = Just ([a], [])
nextPartition a@(a1 : a2 : rest)
  | a2 < a1 - 1 = Just (a, (a1 - 1):(a2 + 1):rest)
  | otherwise = Just (a, h5 (span (>= a1 - 1) rest))
  where
    h5 (_, []) = []
    h5 (xs, aj:ys) =
      let j = length xs + 3 in
      let tweaked = replicate (j - 1) (aj + 1) in
      let a1' = sum (take j a) - sum tweaked in
      a1' : tweaked ++ drop j a

或者認識到Knuth的H3只是展開循環一次,我們可以緊湊地編寫nextPartition:

nextPartition [] = Nothing
nextPartition a@(a1 : rest) =
  Just (a, -- H2
    case (span (>= a1 - 1) rest) of -- H4
      (_, []) -> [] -- H5, termination
      (xs, aj:ys) ->
        a1 + sum (xs) + aj - (length xs + 1) * (aj + 1) -- H6 "Finally..."
        : replicate (length xs + 1) (aj + 1) ++ ys) -- H5/H6 

編輯補充:一年后意外回到這里,看看上面的內容,我不知道為什么我不只是建議這個更簡單的遞歸解決方案:

part m n = part2 (n-m+1) m n
  where
    part2 t m n
      | m == 1 && t == n = [[t]]
      | n < m || n < 1 || m < 1 || t < 1 = []
      | otherwise = [t:r|r <- part2 t (m-1) (n-t)] ++ (part2 (t-1) m n)

暫無
暫無

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

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