Это действительно комментарий на ответ @Aaron Рот, который является хорошим (и способ более эффективен, чем принято отвечать).
Я думаю, вы можете улучшить это, fmap кажется ненужным. Также представление Кнута H5/H6 (ваш шаг) затмевает, что это всего лишь сумма &. Вот версия, которая торчит рядом с именованием Кнута, при попытке сделать алгоритм понятнее:
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 [email protected](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
Или признать, что H3 Кнута просто разворачивая цикл один раз, мы можем написать nextPartition компактно как:
nextPartition [] = Nothing
nextPartition [email protected](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)
Второй replicateM на основе answe r за один день: O – jozefg
И Симонс сказал: выходите и «реплицируйте». –
Это можно улучшить, если сделать 1 replicateM less и вычислить последнее число как (n-sum ls). – Ingo