我正在努力解决发现here
的狮子狼和山羊问题-- Lions Wolves Goats
-- Lion -> Wolf -> Goat :: -1, -1, +1
-- Lion -> Goat -> Wolf :: -1, +1, -1
-- Wolf -> Goat -> Lion :: +1, -1, -1
-- -------------
-- :: -1, -1, -1
这就是我的解决方案:
eat :: (Int, Int, Int) -> (Int, Int, Int)
eat (lions, wolves, goats) = eat (lions - 1, wolves - 1, goats - 1)
eat (0, wolves, goats) = eat (1, wolves - 1, goats - 1)
eat (lions, 0, goats) = eat (lions - 1, 1, goats - 1)
eat (lions, wolves, 0) = eat (lions - 1, wolves - 1, 1)
eat (0, 0, goats) = (0, 0, goats)
此代码编译但代码永远挂起。我很确定我错过了比赛,但我不知道是什么。请让我知道如何正确解决这个问题。
答案 0 :(得分:3)
模式从上到下,左右读取。因此,您的第一个模式与其他模式匹配。重新排序:
eat :: (Int, Int, Int) -> (Int, Int, Int)
eat (0 , 0 , goats) = (0 , 0 , goats )
eat (0 , wolves , goats) = eat (1 , wolves - 1 , goats - 1)
eat (lions , 0 , goats) = eat (lions - 1 , 1 , goats - 1)
eat (lions , wolves , 0) = eat (lions - 1 , wolves - 1 , 1 )
eat (lions , wolves , goats) = eat (lions - 1 , wolves - 1 , goats - 1)
请注意,这假设所有值最初都是正值。如果其中一个是负数,你仍然会得到循环。另请注意,您缺少其他“稳定”状态,并且您的非零包装狮子,狼和山羊的逻辑已关闭。如果有山羊和狼,狮子可能会吃其中任何一只。此外,狼可能会在狮子面前吃一只山羊。
这是非决定论将发挥作用的部分。
答案 1 :(得分:2)
这是一个解决方案,有效但没有整理,因为实现了我想到的第一件事。我相信它可以改进......
让我们定义我们要跟踪的状态(狮子,狼,山羊)
type State = (Int,Int,Int)
饮食规则为
lw,lg,wg :: State -> State
lw (n,w,g) = (n-1,w-1,g+1)
lg (n,w,g) = (n-1,w+1,g-1)
wg (n,w,g) = (n+1,w-1,g-1)
因为在每一步中步骤数将呈指数增长,最好删除冗余状态和不允许的状态(负值),定义辅助函数
nonnegative:: State -> Bool
nonnegative (n,w,g) = n>=0 && w>=0 && g>=0
程序的核心是阶梯函数,我们将规则应用于当前状态
step :: S.Set State -> S.Set State
step s = S.unions [mapfilter lg s, mapfilter lw s, mapfilter wg s]
这里将唯一性外包给Data.Set,使用带有S前缀的限定条件,需要导入
import qualified Data.Set as S (Set,toList,unions,singleton,map,filter,null)
mapfilter正在应用饮食规则并过滤非负效应。
mapfilter f s = S.filter nonnegative $ S.map f s
我们将计算直到我们有解决方案的步骤
solution :: S.Set State -> State
solution s | S.null final = solution $ step s
| otherwise = result s
where final = S.filter done s
其中两个状态元素为零
done (n,w,g) | n==0 = w==0 || g==0
| w==0 = g==0
| otherwise = False
并从Set
中检索第一个结果result :: S.Set State -> State
result s = head $ Prelude.filter done $ S.toList s
使用链接
中引用的初始值initial :: S.Set State
initial = S.singleton (6,55,17)
当我们以初始状态
运行时,我们得到结果Prelude> solution initial
(23,0,0)
那是23只狮子。
<强>更新强>
我清理了一点代码,仍然返回找到的第一个解决方案
import Data.Set (Set,toList,singleton,map,filter,null,union,empty)
type State = (Int, Int, Int) -- lions, wolfs, goats
lw,lg,wg :: State -> State
lw (n,w,g) = (n-1,w-1,g+1)
lg (n,w,g) = (n-1,w+1,g-1)
wg (n,w,g) = (n+1,w-1,g-1)
done,nonnegative :: State -> Bool
done (n,w,g) | n==0 = w==0 || g==0
| w==0 = g==0
| otherwise = False
nonnegative (n,w,g) = n>=0 && w>=0 && g>=0
step :: Set State -> Set State
step s = mapfilters [lg,lw,wg] s
mapfilters :: [State -> State] -> Set State -> Set State
mapfilters [] s = empty
mapfilters (f:fs) s = union (mf f s) (mapfilters fs s)
where mf f s = Data.Set.filter nonnegative $ Data.Set.map f s
solution :: Set State -> Set State
solution s | Data.Set.null final = solution (step s)
| otherwise = final
where final = Data.Set.filter done s
run = head $ toList $ solution $ singleton (6,55,17)
答案 2 :(得分:0)
有效地做到这一点的一个关键是要意识到问题只是看起来不对称。在每个步骤中,有三种可能性:
由于这种对称性,我们只需要考虑数字,而不要过多考虑动物。
由于每一步动物的总数减少一只,最终保留大部分动物的方法是保持步数尽可能小。那么如何将动物数量减少到一个呢?那么,这意味着将其他两种动物降到零。这听起来微不足道,但它实际上是一个关键的洞察力!
假设我们已经知道我们想要生存哪种动物。我们如何尽可能多地获得?我认为你可以说服自己只有两种方法来改善这种情况:要么增加赢家的数量,要么让失败者的数量更加平衡。使失败者的数量较少平衡(唯一的其他选择)将减少获胜者的数量(短期内不好)和延长游戏(从长远来看是不好的)。在每个可以选择的步骤中,这些选择中的任何一个都同样好。因此,只有弄清楚谁应该获胜。
该问题规定了17只山羊,55只狼和6只狮子。关键是17和55都是奇数,而6是偶数。在每一步,所有三个平方都会改变。因此,只有两个可以同时达到零的是山羊和狼。虽然开始只有6只狮子,但它们最终必须存活!每当数字并非都具有相同的平价时,获胜者必须是与其他人不同的人。
当所有三个数字具有相同的奇偶校验时,选择获胜者的最简单方法是计算每种可能性的分数并选择最大值。我们如何计算得分?假设我们选择获胜的山羊,数字标记为g
,l
和w
。所以我们需要|l - w| / 2
平衡动作以平衡狮子与狼。我们还需要(l + w)/2
动作来杀死狮子和狼。所以最后山羊的总数是g - |l - w|/2 + (l + w)/2
。因此
如果狮子比狼群多,最后会有g + w
只山羊。
如果狼比狮子多,那么最后会有g + l
只山羊。
您现在可以在常量时间中计算最终配置,并且可以在线性时间中计算到达它的路径。