sphinx中用的束搜索(Beam Search Algorithm)算法2

来源:互联网 发布:管家婆数据怎么备份 编辑:程序博客网 时间:2024/06/05 10:07

接着上次继续说明束搜索算法,下面是算法示例和算法效率分析

1  算法示例

例1  B=1  (B为束宽度)

下面用表格表示算法流程,用两列表示每一个主循环,第一列每一个加编号的循环显示加入到set的节点,它们按启发权值排序,权值相同的按字母排序,一个节点只被加入到SET一次。第二列每一个编号的循环列出了在第二部分的主循环中SET中的节点,它们将被加入到BEAM中。两列都显示了hash table

 

B = 1

循环编号

SET (编号循环的第一列)
BEAM (编号循环的第二列)

hash_table

 

BEAM = { I(null) }

hash_table = { _, I(null), _, _, _, _, _ }

1

SET = { G(I), J(I), E(I), H(I) }

hash_table = { _, I(null), _, _, _, _, _ }

1

BEAM = { G(I) }

hash_table = { _, I(null), _, _, _, _, G(I) }

2

SET = { D(G), J(G), I(G) }

hash_table = { _, I(null), _, _, _, _, G(I) }

2

BEAM = { D(G) }

hash_table = { _, I(null), _, D(G), _, _, G(I) }

3

SET = { G(D) }

hash_table = { _, I(null), _, D(G), _, _, G(I) }

3

BEAM = { }

hash_table = { _, I(null), _, D(G), _, _, G(I) }

搜索树如下:

 

                             图一   搜索树(起始节点为I,目标节点为B)

算法过程描述:

可以发现hash table 只有7个位置,说明这个示例的内存大小是7.在这三个list中节点的格式为,节点名(前趋名)。搜索图中节点名下的编号是节点的H值。这个例子显示束搜索如何找到从节点I到B的最短路径。此例中B=1,所以每次在BEAM和hash table中最多加入一个节点。

循环1: 搜索I的所有后继节点,找到H值最小的节点加入到BEAM和hash table中;

循环2: 找到G的所有后继节点,从中找到H值最小的节点加入到BEAM和hash table中;

循环3: 寻找D的所有后继节点,G已经存于hash table,不会再重复加入G节点,进入死胡同,BEAM为空,循环结束,没有找到目标节点。

算法结束,得到的路径如下:

                                                                       

说明:BEAM为空,搜索结束,因为节点G已经在hash table中了,所以它不会再加到BEAM。这说明束搜索最大的缺点就是:一个错误的启发函数会导致算法不能找到目标,即使有到目标的路径。增加B的值可以让算法找到目标,但是B太大会让算法在找到目标之前耗尽内存。所以,B值的选取对算法的性能有很大影响。

 

例2  搜索树相同:

                

B=2

循环编号

SET (编号循环的第一列)
BEAM (编号循环的第二列)

hash_table

 

BEAM = { I(null) }

hash_table = { _, I(null), _, _, _, _, _ }

1

SET = { G(I), J(I), E(I), H(I) }

hash_table = { _, I(null), _, _, _, _, _ }

1

BEAM = { G(I), J(I) }

hash_table = { _, I(null), J(I), _, _, _, G(I) }

2

SET = { A(J), D(G), G(J), J(G), E(J), I(G) }

hash_table = { _, I(null), J(I), _, _, _, G(I) }

2

BEAM = { A(J), D(G) }

hash_table = { A(J), I(null), J(I), D(G), _, _, G(I) }

3

SET = { C(A), G(D), J(A) }

hash_table = { A(J), I(null), J(I), D(G), _, _, G(I) }

3

BEAM = { C(A) }

hash_table = { A(J), I(null), J(I), D(G), C(A), _, G(I) }

4

SET = { B(C) [goal found - algorithm returns], A(C) }

hash_table = { A(J), I(null), J(I), D(G), C(A), _, G(I) }

 

算法过程描述:

循环1: 找I的后继节点,选出H值最小的两个节点加入到BEAM和hash table中;

循环2: 找G和J节点的所有后继节点,选出H值最小的两个节点加入到BEAM和hash table中;

循环3: 找A和D节点的所有后继节点,G已经存在于hash table中,故不再重复添加,只加入之前没有的C节点;

循环4: 找到C节点的所有后继节点,找到目标节点,算法结束。

得到的路径如下:

                                   

 

说明:虽然找到了路径,但是不是最优路径,一个错误的启发函数降低了算法的效率。

 

例3  搜索树相同:

    

B=3

循环编号

SET (编号循环的第一列)
BEAM (编号循环的第二列)

hash_table

 

BEAM = { I(null) }

hash_table = { _, I(null), _, _, _, _, _ }

1

SET = { G(I), J(I), E(I), H(I) }

hash_table = { _, I(null), _, _, _, _, _ }

1

BEAM = { G(I), J(I), E(I) }

hash_table = { _, I(null), J(I), _, E(I), _, G(I) }

2

SET = { A(J), C(E), D(G), F(E), G(J), J(E), E(J), H(E), I(E) }

hash_table = { _, I(null), J(I), _, E(I), _, G(I) }

2

BEAM = { A(J), C(E), D(G) }

hash_table = { A(J), I(null), J(I), C(E), E(I), D(G), G(I) }

3

SET = { B(C) [goal found - algorithm returns], A(C), C(A), J(A) }

hash_table = { A(J), I(null), J(I), C(E), E(I), D(G), G(I) }

 

算法过程大体相同,不在赘述。得到的路径如下:

                                        

说明: 更大的束宽度会让算法填满hash table的可用空间。由上图得到的路径显示在最后一级树中,节点A,C,J被添加到SET集合中,然后目标节点B被找到,搜索结束。

 

例4    搜索树相同:

          

 

B=4

循环编号

SET (编号循环的第一列)
BEAM (编号循环的第二列)

hash_table

 

BEAM = { I(null) }

hash_table = { _, I(null), _, _, _, _, _ }

1

SET = { G(I), J(I), E(I), H(I) }

hash_table = { _, I(null), _, _, _, _, _ }

1

BEAM = { G(I), J(I), E(I), H(I) }

hash_table = { H(I), I(null), J(I), _, E(I), _, G(I) }

2

SET = { A(J), C(E), D(G), F(E), G(J), J(E), E(H), H(E), I(E) }

hash_table = { H(I), I(null), J(I), _, E(I), _, G(I) }

2

BEAM = { A(J), C(E), D(G) [not enough memory - algorithm returns] }

hash_table = { H(I), I(null), J(I), A(J), E(I), C(E), G(I) }

 

算法过程大体相同,不在赘述。得到的路径如下:

             

 

说明:hash table在第二次循环当中,当加入D节点时,hash table已满,内存不足,不能把D加入,内存耗尽,算法被迫结束。从得到的路径可以看到,初始时刻hash table中存入起始节点I,第一次循环时又加入了节点G,J,E,H,第二次循环时加入了节点A,C,这时hash table空间已经达到7,D,E候选节点不能加入到hash table中,内存耗尽,算法结束。这说明了束搜索算法的第二个弱点:当B变大时,这个算法消耗内存的速度非常快,就像广度优先搜索算法一样。

 

算法效率/性能分析

图搜索算法的效率/性能分析通常从四个特点分析:完备性,最优性,时间复杂度,空间复杂度。

1  完备性

一般而言,束搜索算法是不完备的,因为它有可能找不到目标节点,如例1。因为它不能给BEAM添加任何节点,即使空间没有耗尽,算法也不能找到目标节点。那么,也就是在时间和空间无限,并且存在从起始节点到目标节点路径的情况下,算法也有可能找不到目标。精确启发函数以及增大束宽度可以提高算法找到目标节点的机会。缺乏完备性是此算法最大的缺点之一。

2  最优性

此算法也不能保证最优性,由例2可以看出,例2中虽然找到了目标节点,但没有找到到达目标的最优路径。这是由于束宽度和启发函数不正确,低估了从每一个节点到达目标节点的消费和邻节点之间的消费,导致算法不能找到最短路径。改正启发函数和增加束宽度可以是算法更有可能找到最优路径。

3  时间复杂度

算法的时间取决于启发函数的准确度,启发函数不准确,会使算法扩展更多的节点寻找目标,而且有可能找不到目标(找到目标之前内存耗尽)。在最坏的情况下,启发函数始终让算法处在搜索树的最大深度,那么最坏的时间复杂度为O(Bm),B是束宽度,m是搜索树中任何路径的最大深度。时间复杂度是线性的,因为算法在每次循环中只扩展B个节点。算法的执行速度是它最大的优点之一。

4  空间复杂度

算法的内存消耗是它最让人满意的特点,因为算法在搜索树的每一层只存储B个节点,最坏的空间复杂度是OBm),B是束宽度,m是搜索树中任何路径的最大深度。线性的内存消耗允许算法用于大搜索空间的搜索,寻找潜在的目标,这是其它算法无法与之媲美的。

那些地方有误,还请大家批评指正,互相学习。 

原文链接 :http://jhave.org/algorithms/graphs/beamsearch/beamsearch.shtml

 

 

 

 

 

 

 

 

原创粉丝点击