﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-jifengjingyun</title><link>http://www.cppblog.com/jifengjingyun/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 20:11:00 GMT</lastBuildDate><pubDate>Tue, 09 Jun 2026 20:11:00 GMT</pubDate><ttl>60</ttl><item><title>启发式搜索和A*</title><link>http://www.cppblog.com/jifengjingyun/archive/2011/07/01/149914.html</link><dc:creator>jifengjingyun</dc:creator><author>jifengjingyun</author><pubDate>Fri, 01 Jul 2011 08:41:00 GMT</pubDate><guid>http://www.cppblog.com/jifengjingyun/archive/2011/07/01/149914.html</guid><wfw:comment>http://www.cppblog.com/jifengjingyun/comments/149914.html</wfw:comment><comments>http://www.cppblog.com/jifengjingyun/archive/2011/07/01/149914.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jifengjingyun/comments/commentRss/149914.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jifengjingyun/services/trackbacks/149914.html</trackback:ping><description><![CDATA[<div></div>
<div class="bct fc05 fc11 nbw-blog ztag js-fs2">
<p><wbr><font style="color: rgb(255,0,255); font-weight: bold" size="3">转载请注明作者：<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#112;&#104;&#121;&#108;&#105;&#112;&#115;&#64;&#98;&#109;&#121;"><font color="#189a0f">phylips@bmy</font></a></font></p>
<p><strong><font color="#ff00ff" size="3">出处：<a href="http://duanple.blog.163.com/blog/static/70971767200811344425606/"><font color="#189a0f">http://duanple.blog.163.com/blog/static/70971767200811344425606/</font></a></font></strong></p>
<p><font style="color: rgb(255,0,255); font-weight: bold" size="3">引言：</font><br />现实中的很多问题都可以看成搜索问题<br />众里寻她千百度，蓦然回首那人却在灯火阑珊中。<br />就算是人类的思维，很多时刻也是在进行着搜索，搜索存储在脑中的记忆元素<br />就算身在社会中，我们也还是在不同的时间搜索着不同的东西。<br /><br />这些只是普遍意义上的搜索，在算法中，很多问题时间上是在进行着状态空间搜索，希望找到一条从初始状态到目标状态的排序，比如排序，8数码，8皇后，最短路<br /><br />而进行搜索的方法有很多，最简单的如盲目搜索，与之相反的一种就是启发式搜索，就是有效的利用当前的信息，根据一个评估函数，选择一个比较有前途的待扩展节点进行扩展。启发式搜索也叫做A算法，而A*算法则是一种特殊形式的A算法。这也算是人工智能的一个分支内容，网上的一些资料，比如lrj的那本黑书，还有一些介绍A*算法的文章，基本上都是A*算法的应用，所以很多概念，并不准确，而对于理解A*算法需要的一些关键性的证明也是没有提供的。<br /><br />本文，就从人工智能的角度来介绍这个算法，主要内容参考自NJ 尼尔森的<a href="http://www.ebookdown.net/Software/Catalog124/1466.html" target="_blank"><font color="#189a0f">&lt;&lt;人工智能&gt;&gt;</font></a>，这本书对启发式算法进行了比较深入的解释，关键是其中的一些关键性的证明都是存在的，比如证明A*算法的可接纳性，一致性条件及定理。<br /><br /><font size="3"><span style="color: rgb(255,0,255); font-weight: bold">关键概念：</span></font><br /><br />介绍一些关键的概念：<br />首先，对于状态空间搜索，是可以提出一个通用的搜索算法框架的，而这个框架中则主要使用了open,和closed两个表。open表，保存了当前待扩展节点，closed表则保存已扩展节点，而BFS DFS也可以纳入这个框架，比如DFS中使用的white black gray染色，实际于 open closed便是对应的。<br /><br />对于启发式搜索一般具有如下评估函数f*(n) = g*(n)+h*(n)，f*(n)表示从起始点经过n到达目标的估计函数<br />g*(n)表示从起始点到达节点n的花费函数，比如对于最短路问题，可能就是已经发现的从出发点，到当前节点的最小cost， h*(n)表示从当前状态n到目标状态的估计花费。<br /><br />对于h*(n)施加如下约束，g*(n) &gt;= g(n)&nbsp; ,h*(n) &lt;= h(n),即估计花费从是小于等于实际最小花费，则启发式搜索就是A*算法。而A*算法是可接纳的，也就是说如果存在一条到达目标的最优路径，A*算法可以保证找到这条路径。<br /><br />继续对A*算法的h函数施加约束，可以保证满足一致性，或者叫单调性，约束是：对于任意的状态ni,nj<br />h*(ni) - h*(nj) &lt;= c(ni,nj)<br />一致性可以保证，当 A*扩展节点n时，它已经找到了到达节点n的最优路径。<br /><br /><font style="color: rgb(255,0,255); font-weight: bold" size="3">通用搜索框架：</font><br />1）生成一个包含初始点n0的搜索树Tr,并把n0放入open表<br />2）初始化一个为空的closed表<br />3）如果open表为空，则失败退出<br />4）取出open表第一个元素，将该元素放入closed表，设该元素为n<br />5）如果n为目标，则可以从n沿着Tr的弧进行回溯，找到解决方案，成功退出<br />6）扩展节点n，生成n的后继节点集M，可以利用一个parent指针，将这些节点指向n，从而维护Tr树，将这些节点放入open表<br />7）按任意模式或者启发式方式对open表进行排序<br />8）返回步骤3<br /><br />该算法可以用来执行 DFS BFS 以及A*，如果是BFS则只需要把待扩展 节点放到open表的尾部即可<br />如果是DFS 则只需要把待扩展 节点放到open表的头部即可，均不需要执行7）<br />而对于启发式搜索来说，要根据启发函数进行步骤7）。<br /><br /><br /><font size="3"><span style="color: rgb(255,0,255); font-weight: bold">A*算法中可能需要解决的问题：</span></font><br />观察以上步骤，实际上在加入两个表的操作中，可能遇到一些特殊情况，就是原表中已经包含了当前要插入的这个点。这时候该如何处理。对应的在搜索树，或者搜索图中，行进的过程中又到达了原来曾经扩展或者已经在待扩展open表的那个点。<br /><br />比如在8数码中，实际上每两个状态如果可达，则肯定上可以相互到达的，也就是说对于节点n的每个后继都可以吧节点n作为它的后继。这样我们就需要解决这类问题，对于这种情况只要，保证在扩展节点的后继M中不要包含它的双亲就行了，考虑到更长的循环，我们还要把双亲修改成祖先，即后继M中不要包含它的祖先。这样我们可能就需要一个数据结构用来保存节点的祖先。<br /><br />但是仍有可能通过不同的路径到达相同的节点，解决这个问题的一个简单方法就是忽略它，不去检查。也就是说不去检查是否一个节点已经存在于open 或者closed中。这样实际上就允许通过不同的路径到达相同的节点，这个相同节点在Tr中重复多次出现，而这个重复的次数就是算法发现到达该点的不同路径的数目。<br /><br />当然，如果对A*算法，加入一致性约束，这样就可以保证当A*首次扩展节点n时，它已经找到了到达该节点的最佳路径。<br /><br />为了防止不满足一致性约束的A*算法进行重复搜索，需要对A*算法进行修改，由于可以沿着不同路径到达同一点，导致A*算法实际上产生的是一个搜索图G，而不是树，而树Tr刚好包含在这个图里。之所以要保留这个图，是因为可能后面的那个路径要比现在这个更优，在发现这样的路径时我们要更新那个搜索树。<br /><br /><font style="color: rgb(255,0,255); font-weight: bold" size="3">A*算法框架：</font><br />1）生成一个包含初始点n0的搜索树Tr,并把n0放入open表<br />2）初始化一个为空的closed表<br />3）如果open表为空，则失败退出<br />4）取出open表第一个元素，将该元素放入closed表，设该元素为n<br />5）如果n为目标，则可以从n沿着Tr的弧进行回溯，找到解决方案，成功退出<br />6）扩展节点n，生成n的后继节点集M，在G中n的祖先不能出现在m中，在G中放置M，使他成为n的后继<br />7）<br />从M中每一个不在G中的节点放置一个指向n的指针，并且加入open中(即不在open也不在closed中的节点)<br />对于在open的那些，则判断是否需要更新，如果更小则更新<br />对于在closed中的那些，则判断是否需要更新，如果更小则更新<br />8）按照启发式方式对open表进行递增排序<br />9）返回步骤3<br /><br />A*算法的一些证明和性质：<br /><br />对图和h函数施加一些条件，可以保证A*算法得到最优解：<br />1）图中每个节点的后继是有限的<br />2）图中所有弧的代价都大于某个正数<br />3）对于图中所有节点，h函数&lt;= 实际代价<br /><br />证明略。<br />主要依赖于如下引理：<br /><a href="http://img.blog.163.com/photo/RLEa-tneYuEN7fim4bsStg==/885801751709358107.jpg" target="_blank"><img style="width: 459px; height: 80px" alt="启发式搜索和A* - 星星 - 银河里的星星" src="http://img.blog.163.com/photo/RLEa-tneYuEN7fim4bsStg==/885801751709358107.jpg" __1309509216181__="ev_1918416052" /></a><br /><br />h函数取值越小，所具有的启发性越小，而查看的节点就越多，dikstra算法实际上就是h函数=0，的特殊的A*算法。<br /><br />一致性约束。。。<br /><br />IDA*算法。<br />迭代加深的A*算法，初始设置截断值为h*(n0),以后逐步增大<br /></p></div><img src ="http://www.cppblog.com/jifengjingyun/aggbug/149914.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jifengjingyun/" target="_blank">jifengjingyun</a> 2011-07-01 16:41 <a href="http://www.cppblog.com/jifengjingyun/archive/2011/07/01/149914.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>