我希望有人可以帮助弄清楚我的错误所在。调用g 3 4 0 2 (M.empty,0) []
,我希望[[2,1,0,1]]
成为结果。相反,我看到[[2,1,0,1],[2,1,0,1]]
。
该程序应该通过每次向列表添加不同的数字来累积长度为m
的不同数字模式,在到达n-1
时向下返回,在到达0
时向上返回。当向上和向下方向递归调用函数时,明显的问题发生在中间。
如果我这样评论第11行:
else g n m (digitCount + 1) (lastDigit + 1) (hash',hashCount') (lastDigit:digits)
-- g n m (digitCount + 1) (lastDigit - 1) (hash',hashCount') (lastDigit:digits)
我得到了正确的结果[]
当评论第11行并将第10行修改为:
时else g n m (digitCount + 1) (lastDigit - 1) (hash',hashCount') (lastDigit:digits)
再次,正确的结果[[2,1,0,1]]
为什么在使用g
运算符调用++
两次时,我得到两个[2,1,0,1]
而不是一个?在我的想法中,g
中的每个结果都应该是不同的,因为在任何递归调用中,不同的数字顺序是(或应该)累积。
提前致谢。
import qualified Data.Map as M
g :: Int -> Int -> Int -> Int -> (M.Map Int Bool, Int) -> [Int] -> [[Int]]
g n m digitCount lastDigit (hash,hashCount) digits
| digitCount == m = if test then [reverse digits] else []
| otherwise =
if lastDigit == 0
then g n m (digitCount + 1) (lastDigit + 1) (hash',hashCount') (lastDigit:digits)
else if lastDigit == n - 1
then g n m (digitCount + 1) (lastDigit - 1) (hash',hashCount') (lastDigit:digits)
else g n m (digitCount + 1) (lastDigit + 1) (hash',hashCount') (lastDigit:digits)
++ g n m (digitCount + 1) (lastDigit - 1) (hash',hashCount') (lastDigit:digits)
where test = hashCount == n
(hash',hashCount') =
if test
then (M.empty,hashCount)
else case M.lookup lastDigit hash of
Just anyting -> (hash,hashCount)
Nothing -> (M.insert lastDigit True hash,hashCount + 1)
答案 0 :(得分:3)
现在你已经有了它,这是一种更通用的方法。
我们需要走解决方案之树。
data S a = Solution a | Explore [S a]
解决方案是这棵树的叶子,探索是要探索的选项列表。
-- this is very much unfoldr-like
generator :: [S a] -> [a]
generator [] = []
generator (Solution a: ss) = a: generator ss
generator (Explore ps: ss) = generator $ ss ++ ps
现在,给出一个“可能解决方案”列表,生成一个解决方案列表。生成器模式匹配Explores,并将要探索的解决方案列表附加到列表的末尾。通过这种方式,我们正在探索广度优先的解决方案,这样我们就可以处理非终止分支。 (深度优先不能离开非终止分支)。这当然是以牺牲内存为代价的,但即使对于无数解决方案的问题,您也可以找到有限数量的解决方案。
现在,为您的问题生成解决方案的函数:
g :: Int -> Int -> [S [Int]]
g n m = [Explore $ g' [i] (S.singleton i) | i <- [1..n-1]] where
g' is@(h:_) ms
| h < 0 || h >= n || length is > m = [] --no solution, nothing to explore
| otherwise = maybeSolution ++
[ Explore $ g' ((h-1):is) $ S.insert (h-1) ms
, Explore $ g' ((h+1):is) $ S.insert (h+1) ms ]
where
maybeSolution
| S.size ms == n = [Solution is]
| otherwise = []
给定n和m,生成一个要探索的子树列表。 g'是生成子树列表的辅助函数,给定已生成的Int列表和已使用的Int集合。因此,有一个明确的终止条件:我们在所需范围之外附加一个数字,或者列表变得太长 - 再探索不能产生解,所以返回[]。否则,我们在界限内,也许解决方案看到Ints列表是否已经是一个有效的解决方案,并建议更多的子树来探索。
main = print $ map reverse $ generator $ g 3 6
你的问题解决了。
答案 1 :(得分:2)
在最后一个分支中对g
和(++)
的两次递归调用中,除了lastDigit
之外,您传递的参数完全相同。
递归的基本情况不会显示lastDigit
- 它只是比较m
和digitCount
,n
和hashCount
,然后返回{ {1}}。
因此,在[reverse digits]
案例被立即命中,然后返回(++)
的基本案例的情况下,您将获得相同的值重复。
我没有完全理解您的问题规范,但您可能需要在进行递归调用时将[reverse digits]
的“新”值添加到数字中 - 即lastDigit
或(lastDigit-1):digits
答案 2 :(得分:2)
为什么当使用++运算符调用g两次时,我得到两个[2,1,0,1]而不是仅仅 一?在我的思考中,g中的每个结果都应该是不同的,因为在任何递归调用中,a 不同的数字顺序是(或应该)累积。
但是你的两个(Map,Int)在两个调用中是相同的,所以递归调用不知道另一个调用找到了什么。考虑调用g ...(lastDigit-1)。它也会调用g ...(lastDigit)(通过向它添加1到(lastDigit-1)),并按照分支g ...(lastDigit + 1)产生相同的结果。
另外,(Map a())是一个(Set a),因为你不使用map中的Bool值,它与()相同:
import qualified Data.Set as S
g :: Int -> Int -> Int -> Int -> (S.Set Int, Int) -> [Int] -> [[Int]]
g n m digitCount lastDigit (hash,hashCount) digits
| digitCount == m = if test then [reverse digits] else []
| lastDigit < 0 || lastDigit == n = []
| otherwise = g n m d' (lastDigit + 1) h' (lastDigit:digits)
++ g n m d' (lastDigit - 1) h' (lastDigit:digits)
where test = hashCount == n
d' = digitCount + 1
h'
| test = (S.empty,hashCount)
| S.member lastDigit hash = (hash,hashCount)
| otherwise = (S.insert lastDigit hash,hashCount + 1)