解决狮子,狼和山羊

时间:2016-03-19 02:09:47

标签: haskell

我正在努力解决发现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)

此代码编译但代码永远挂起。我很确定我错过了比赛,但我不知道是什么。请让我知道如何正确解决这个问题。

3 个答案:

答案 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)

有效地做到这一点的一个关键是要意识到问题只是看起来不对称。在每个步骤中,有三种可能性:

  1. 只剩下一种动物。这是最终状态。
  2. 剩下两种动物。在这种情况下,会创建一个缺失动物的样本,并且这两个预先存在的类型的数量减少一个。
  3. 所有三种动物仍然存在。在这种情况下,其中一个数量增加,而另外两个数量减少。
  4. 由于这种对称性,我们只需要考虑数字,而不要过多考虑动物

    由于每一步动物的总数减少一只,最终保留大部分动物的方法是保持步数尽可能小。那么如何将动物数量减少到一个呢?那么,这意味着将其他两种动物降到零。这听起来微不足道,但它实际上是一个关键的洞察力!

    假设我们已经知道我们想要生存哪种动物。我们如何尽可能多地获得?我认为你可以说服自己只有两种方法来改善这种情况:要么增加赢家的数量,要么让失败者的数量更加平衡。使失败者的数量较少平衡(唯一的其他选择)将减少获胜者的数量(短期内不好)和延长游戏(从长远来看是不好的)。在每个可以选择的步骤中,这些选择中的任何一个都同样好。因此,只有弄清楚谁应该获胜。

    该问题规定了17只山羊,55只狼和6只狮子。关键是17和55都是奇数,而6是偶数。在每一步,所有三个平方都会改变。因此,只有两个可以同时达到零的是山羊和狼。虽然开始只有6只狮子,但它们最终必须存活!每当数字并非都具有相同的平价时,获胜者必须是与其他人不同的人。

    当所有三个数字具有相同的奇偶校验时,选择获胜者的最简单方法是计算每种可能性的分数并选择最大值。我们如何计算得分?假设我们选择获胜的山羊,数字标记为glw。所以我们需要|l - w| / 2平衡动作以平衡狮子与狼。我们还需要(l + w)/2动作来杀死狮子和狼。所以最后山羊的总数是g - |l - w|/2 + (l + w)/2。因此

    1. 如果狮子比狼群多,最后会有g + w只山羊。

    2. 如果狼比狮子多,那么最后会有g + l只山羊。

    3. 您现在可以在常量时间中计算最终配置,并且可以在线性时间中计算到达它的路径。