<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
					xmlns:content="http://purl.org/rss/1.0/modules/content/"
					xmlns:wfw="http://wellformedweb.org/CommentAPI/"
				  >
<channel>
<title><![CDATA[Saturn's Weblog - 标签：搜索]]></title>
<link>http://www.cnsaturn.com/tag/&aelig;&ccedil;&acute;&cent;</link>
<description><![CDATA[Saturn's weblog, STBlog官方站点]]></description>
<language>zh-CN</language>
<pubDate>Mon, 06 Sep 2010 11:34:43 -0400</pubDate>
<item>
<title><![CDATA[使用回溯（Backtracking）算法解决狼，羊和卷心菜过河问题]]></title>
<link>http://www.cnsaturn.com/posts/using-backtracking-algorithm-to-solve-wolf-sheep-cabbage-problem</link>
<pubDate>Wed, 09 Jun 2010 15:03:00 -0400</pubDate>
<description><![CDATA[<p>
 <a href="http://en.wikipedia.org/wiki/Backtracking">回溯算法（Backtracking algorithm）</a>是机器搜索（Search）的基本算法之一，通常用于解决具有唯一解，但有多个限制条件的复杂问题。</p>
<p>
 以下是狼，羊和卷心菜问题的描述：</p>
<p>
 一个人需要用一条船将狼，羊河卷心菜运送到一条河的对岸。限制条件如下：</p>
<ul>
 <li>
  这条船比较小，每次仅能运送一个物体，且船的运行都需要人来控制</li>
 <li>
  当没人看管的时候，狼会吃羊，而羊会吃卷心菜。</li>
</ul>
<p>
 如何将这些这三个物体安全送到河的对岸？</p>
<p>
 对这个问题感兴趣的朋友可以<a href="http://www.plastelina.net/examples/games/CHN.html">玩玩这个逻辑游戏</a>，看你玩多少次能够将他们成功运送到对岸。</p>
<p>
 下面看看，如何使用Backtrack算法合理的分析和解决这个问题：</p>
<p>
 首先，我们对这个问题里面涉及到的状态图形化（State Representation），这样有助于我们理解问题和编写程序解决问题。</p>
<p style="text-align: center;">
 <img alt="start-goal.png" src="http://www.cnsaturn.com/uploads/start-goal.png" /></p>
<p>
 上图分别描述了我们的初始状态和目标状态，其中的字母和数字分别表示：</p>
<ul>
 <li>
  B 表示船的位置，在左边那个栏位时，表示船在起始岸边；反之，表示船在目标岸边；</li>
 <li>
  C表示cabbage（卷心菜），W表示wolf（狼），S表示sheep（羊）。</li>
 <li>
  数字表示在当前岸边物体的个数</li>
</ul>
<p>
 其次，我们要确定能够对状态进行的操作（Operators），显而易见，不管穿在那边，都可以进行如下四种操作：</p>
<ul>
 <li>
  M(W)：将狼运到对岸</li>
 <li>
  M(S): 将羊运到对岸</li>
 <li>
  M(C)：将卷心菜运到对岸</li>
 <li>
  M()：不运送任何东西，只将穿运送到对岸</li>
</ul>
<p>
 然后，我们要知道某个状态是否成功，即当前状态就是目标状态，还需要一个目标测试函数（Goal test），这个问题比较简单，不需要函数来表达，简单来说判断一个状态是否成功，需要判断起始岸的狼，羊和卷心菜数量之和为0，即以下函数：</p>
<p>
 N(C @ start) ＋ N(W @ start) + N(S @ start) = 0</p>
<p>
 以上函数，N表示数量，如N(C @ start)表示在起始岸上C的数目。</p>
<p>
 最后，在真正应用回溯（backtrack）算法之前，我们还需要确定一个或多个启发函数（heuristic function），这个函数如其名，用来指导和推测整个推导过程是否朝着正确的方向执行。这个案例的启发函数可以简单描述为：让目标岸边的狼，羊，卷心菜之和最大，即为3。</p>
<p>
 这是我们能从问题中发现的最直接的推导，好像没什么用。但对于一些没有固定答案的问题来说，它却是我们判断程序运行是否结束的依据（如果你将这种问题丢给程序去运行解决的话）。</p>
<p>
 那么没有这个函数，我们如何找出更有用的启发函数让程序知道如何最快的找到最优解。这个就需要分析具体问题了，如果你认真分析过这个问题，你会得到以下隐含条件：</p>
<p>
 <strong>狼是重口味，它不管在岸的哪一边都是不会吃卷心菜的。</strong><strong>所以，<span style="color: rgb(255, 0, 0);">我们需要先将狼和卷心菜运送到对岸。</span></strong></p>
<p>
 这实际上我们能找到的另一个heuristic function，也可以称为一个子目标（sub goal）。有了它做指导，这个问题就迎刃而解了。</p>
<p>
 有了以上分析，我们只需要从初始状态开始，依次应用上面描述过的4个操作。如果该操作违反了问题中的限制条件，则需要将状态回推到上一个状态，并应用下一个操作。直到我们找到最后的目标状态，再将所有成功状态连在一起，就是我们的解决方案了。</p>
<p>
 （To be updated：需要补上backtrack流程图）</p>]]></description>
