我试图在haskell中创建一个带有数字a的函数,使用列表对其进行分配,即对于数字4
,它将创建[[1,1,1,1],[1,1,2],[1,3],[2,2],[4]]
。我正在考虑使用列表理解,它会创建列表x,然后使用[1 ... n]中的数字创建更多列表(n是我想要的分区号),其中创建的列表总和相等到n。
到目前为止我创建的代码是 -
partions (n:xs) = [[x|x<-[1...n], sum[x]==n]]|xs<-[1..]]
但很明显它不起作用,有什么建议吗?
感谢。
答案 0 :(得分:4)
我建议尝试递归:要获得n的分区,迭代数字i = 1到n,并递归生成(ni)的分区,基本情况是1的唯一分区本身是1,并且0的分区是空列表。
答案 1 :(得分:3)
这个怎么样......
import Data.List (nub, sort)
parts :: Int -> [[Int]]
parts 0 = []
parts n = nub $ map sort $ [n] : [x:xs | x <- [1..n`div`2], xs <- parts(n - x)]
试一试:
*Main Control.Monad> forM [1..5] (print . parts)
[[1]]
[[2],[1,1]]
[[3],[1,2],[1,1,1]]
[[4],[1,3],[1,1,2],[1,1,1,1],[2,2]]
[[5],[1,4],[1,1,3],[1,1,1,2],[1,1,1,1,1],[1,2,2],[2,3]]
我认为这是正确的,如果效率不高的话。
答案 2 :(得分:2)
我对Haskell有点生疏,但也许以下代码可以指导您找到解决方案。
parts :: Int -> Int -> [[Int]]
parts 0 p = [[]]
parts x p = [(y:ys) | y <-[p..x], ys <- (parts (x - y) y)]
然后你必须调用x = n和p = 1的部分。
修改强>
当x等于0时,我修复了基本情况,以返回包含单个项目的列表,该项目为空列表。现在它工作正常:))
答案 3 :(得分:2)
我发现定义一个辅助函数partitionsCap
很有帮助,它不允许任何项大于给定值。递归使用,它只能用于产生您想要的monotonically递减结果(例如,当您已经[1,3,1]
时没有[1,1,3]
):
partitions :: Int -> [[Int]]
partitions n = partitionsCap n n
partitionsCap :: Int -> Int -> [[Int]]
partitionsCap cap n
| n < 0 = error "partitions: negative number"
| n == 0 = [[]]
| n > 0 = [i : p | i <- [hi,hi-1..1], p <- partitionsCap i (n-i)]
where hi = min cap n
算法的核心是,在对N进行分区时,将i
从n
降低到1,并将i
添加到n-i
的分区}。简化为:
concat [map (i:) $ partitions (n-i) | i <- [n,n-1..1]]
但错了:
> partitions 3
[[3],[2,1],[1,2],[1,1,1]]
我们希望[1,2]
消失。因此,我们需要限制我们前面提到的分区,以便它们不会超过i
:
concat [map (i:) $ partitionsCap i (n-i) | i <- [hi,hi-1..1]]
where hi = min cap n
现在,要清理它: concat 和 map 如此接近,引起了我的注意。一点背景:列表推导和列表monad是very closely related, concatMap 与>>=
相同,其参数在列表monad中翻转。所以我想知道:那些 concat 和 map 能不能以某种方式变成>>=
,并且>>=
能以某种方式甜言蜜语地进入列表理解?
在这种情况下,答案是肯定的: - )
[i : p | i <- [hi,hi-1..1], p <- partitionsCap i (n-i)]
where hi = min cap n