图形演算

内容纲要

简介

在离散数学、算法与人智能领域,很多问题可以表示成「节点与连线所形成的图形」,一个程序要解决某个问题其实是在这个图形里把目标节点给找出来,于是问题求解就简化成了图形的搜索,我们只要把解答给「找出来」就行了。

图形搜索的方法大致可以分为「深度优先搜寻(Depth-First Search, DFS)、广度优先搜寻(Breath-First Search, BFS)、最佳优先搜寻(Best-First Search, BestFS) 三类。

然后针对最佳优先搜寻的部份,还有一种具有理论背景,相对强大好用的A* 算法可用。

图形的表达

图形是由节点(node) 与连线(edge) 所组成的。举例来说,以下是一个包含六个节点与十条连线的简单图形:


图形演算

深度优先搜寻

所谓的「深度优先搜寻」 (Depth-First Search, DFS),就是一直往尚未访问过的第一个邻居节点走去的一种方法,这种方法可以采用程式设计中的「递归」完成,以下是深度搜寻的演算法:

function dfs(g, node) {
  if (g[node].v !=0) return;   // 如果已访问过,就不再访问
  printf("%d=>", node);       // 否则,打印节点
  g[node].v = 1;              //   并设定为已访问
  var neighbors = g[node].n;  // 取出相邻节点
  for (var i in neighbors) {  // 对于每个相邻节点逐一访问
  }
}

针对上述的图形,若采用深度优先搜寻,其结果可能如下所示(图中红色的数字代表访问顺序)


图形演算

广度优先搜寻

虽然深度优先搜寻可以搜寻整个图形,但是却很可能绕了很久才找到目标,于是从起点到目标可能会花费很久的时间(或说路径长度过长)。

如果我们想找出到达目标最少的步骤,那么就可以采用「广度优先搜寻」 (Breath-First Search, BFS) 的方式。

广度优先搜寻BFS 是从一个节点开始,将每个邻居节点都一层一层的拜访下去,深度最浅的节点会优先被拜访的方式。

举例而言,针对上述的图形示例,若采用「广度优先搜寻BFS 」的方式,那么拜访顺序将会如下所示:


图形演算

要能用程式进行广度优先搜寻,必须采用「先进先出」(First-in First-Out, FIFO) 的方式管理节点,因此通常在「广度优先搜寻」里会有个伫列(queue) 结构,以下是BFS 的演算法片段:

function bfs(g, q) { // 广度优先搜索
  if (q.length == 0) return; // 如果 queue 已空,则返回。
  var node = dequeue(q);     // 否则、取出 queue 的第一个节点。
  if (g[node].v == 0)        // 如果该节点尚未拜访过。
    g[node].v = 1;           //   标记为已拜访
  else                       // 否则 (已拜访过)
    return;                  //   不继续搜寻,直接返回。
  printf("%d=>", node);      // 印出节点
  var neighbors = g[node].n; // 取出邻居。
  for (var i in neighbors) { // 对于每个邻居
    var n = neighbors[i];
    if (!g[n].visited)       // 假如该邻居还没有被拜访过
      q.push(n);             //   就放入 queue 中
  }
  bfs(g, q);
}

最佳优先搜寻

但是、上述两个方法其实都还不够好,深度搜寻会洪水猛兽一样乱冲乱撞,而广度搜寻则会耗费太多的资源,并且没有效率,无法很快的找到目标点。

假如我们能够知道哪些点距离目标点最近,也就是哪些点比较好的话,那就能采用「最佳优先搜寻(Best-First Search) 的方式来搜寻了。

最佳优先搜寻的实作方法与广度优先搜寻类似,但是并不采用伫列(queue) ,而是采用一种根据优先程度排序的结构,每次都取出最好的那个继续进行搜寻。

但是、节点的好坏通常很难评估,单纯采用某种距离去评估往往会过度简化问题,这点往往是最佳优先搜寻的困难之所在。

还好,有时我们不需要非常精确的评估,只要问题符合$h(x) \leq d(x,y) + h(y) $这样的单调(monotone)特性,就可以使用A算法来进行较快速的搜寻,这种方法比广度优先搜寻通常快很多,因为A不会搜寻所有节点,而是有系统的朝着整体较好的方向前进,这种方法在电脑游戏(Game)上常被用在NPC (非人类角色)的智慧型搜寻行为设计上面,是人工智慧搜寻方法中较强大的一种。

code enjoy! ٩(๑❛ᴗ❛๑)۶

作者:indeex

链接:http://indeex.cc

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


发表评论

您的电子邮箱地址不会被公开。