实验一:搜索算法问题求解
硬件:计算机
软件:操作系统:WINDOWS
应用软件:C++
使用搜索算法实现罗马尼亚问题的求解
1) 创建搜索树;
2) 实现搜索树的宽度优先搜索,深度优先搜索,一致代价搜索,迭代加深的深度优先搜索算法;
3) 实现贪婪最佳优先搜索和A*搜索
4) 使用编写的搜索算法代码求解罗马尼亚问题;
5) 记录各种算法的时间复杂度并绘制直方图
1) 宽度优先搜索,深度优先搜索,一致代价搜索,迭代加深的深度优先搜索算法哪种方法最优?
2) 贪婪最佳优先搜索和A*搜索那种方法最优?
3) 分析比较无信息搜索策略和有信息搜索策略。
罗马尼亚问题中的每一个城市的城市信息:
罗马尼亚问题中有多个城市,城市和城市之间是有直接或者间接的联系,而且每个城市和目标城市也有相关的联系,所以需要全面具体地记录保存罗马尼亚问题中的每个城市的城市信息,方便后期的各种搜索算法和搜索树的构建。
每一个城市的城市信息应该包含:
每一个城市的城市信息保存代码实现如下:
typedef pair<string,int> neighbor; //当前城市的相邻城市的城市名和对应的路径代价
struct state //罗马尼亚问题中的每一个城市的城市信息
{
string name; //该城市的城市名
int neighbor_num; //与该城市相邻的有几个城市
map<int,neighbor> nextstate; //记录每个相邻的城市和对应的路径代价
int h; //该城市到目标城市的最小代价路径的估计值
};
罗马尼亚问题的图:
在罗马尼亚问题中,我们需要知道总共有多少个城市并记录保存。我们使用 邻接表 法对罗马尼亚问题的图进行保存,即一个城市名对应该城市的城市信息,可以根据该城市的信息到达其相邻的城市,即完成整个罗马尼亚问题的图。
在进行图搜索的过程中,我们需要知道一个城市是否已经被讨论并拓展过,所以需要记录保存各个城市的状态,得知该城市是否已经被拓展过。
罗马尼亚问题的图的各个变量定义如下:
int state_num = 0; //罗马尼亚问题中的每一个城市
map<string,state> graph; //邻接表保存罗马尼亚问题的图:<城市名,城市信息>
map<string,int> explored; //罗马尼亚问题中的城市是否被拓展过
罗马尼亚问题的图的构建:
首先,知道罗马尼亚问题的图中有多少个城市,然后再依次对每一个城市的城市信息进行初始化,输入各个城市的城市信息过程包括:
在对各个城市的城市信息输入结束后,在罗马尼亚问题的图中添加该城市,即将城市名和对应城市的城市信息保存在图中,并且初始化当前城市未被拓展过,然后继续下一个城市的城市信息输入。
罗马尼亚问题的图的构建代码实现如下(文件输入):
void File_Input(string s)
{
ifstream f(s); //导入描述罗马尼亚问题详细信息的文件
f >>state_num; //输入罗马尼亚问题的总城市数目
for(int i=0;i<state_num;i++){ //初始化每一个城市的城市信息
state t;
f >> t.name; //当前城市的城市名
f >> t.h; //当前城市到目标城市的最小代价路径的估计值
f >> t.neighbor_num; //当前城市有几个相邻的城市
for(int j=0;j<t.neighbor_num;j++){ //保存每一个相邻的城市
f >>t.nextstate[j].first; //保存相邻城市的城市名
f >>t.nextstate[j].second; //保存到相邻城市的路径代价
}
graph[t.name] = t; //在罗马尼亚问题的图中添加当前城市
explored[t.name] = 0; //初始化当前城市未被拓展过
}
}
描述罗马尼亚问题详细城市信息的文件( Romania.txt )如下:
20
Oradea 380 2 Zerind 71 Sibiu 151
Zerind 374 2 Oradea 71 Arad 75
Arad 366 3 Zerind 75 Sibiu 140 Timisoara 118
Sibiu 253 4 Oradea 151 Arad 140 Fagaras 99 RimnicuVilcea 80
Timisoara 329 2 Arad 118 Lugoj 111
Lugoj 244 2 Timisoara 111 Mehadia 70
Mehadia 241 2 Lugoj 70 Dobreta 75
Dobreta 242 2 Mehadia 75 Craiova 120
Fagaras 176 2 Sibiu 99 Bucharest 211
RimnicuVilcea 193 3 Sibiu 80 Craiova 146 Pitesti 97
Craiova 160 3 Dobreta 120 Pitesti 138 RimnicuVilcea 146
Pitesti 100 3 Craiova 138 Bucharest 101 RimnicuVilcea 97
Bucharest 0 4 Fagaras 211 Pitesti 101 Giurgiu 90 Urziceni 85
Giurgiu 77 1 Bucharest 90
Urziceni 80 3 Bucharest 85 Hirsova 98 Vaslui 142
Hirsova 151 2 Urziceni 98 Eforie 86
Eforie 161 1 Hirsova 86
Vaslui 199 2 Urziceni 142 Iasi 92
Iasi 226 2 Vaslui 92 Neamt 87
Neamt 234 1 Lasi 87
搜索树中的树结点定义:
在搜索树中,每一个树结点应该记录的信息包括:
搜索树中的树结点代码实现:
struct Treenode //搜索树中的树结点定义
{
int child_num; //该树结点有几个孩子结点
int value; //该树结点的代价估计值(启发式)
int depth; //该树结点的深度
state theState; //该树结点对应在哪个城市
Treenode * father; //保存该树结点的父亲结点(指针)
map<int,Treenode *> child; //保存该树结点的所有孩子结点(指针)
};
创建一个新的树结点:
创建一个新的树结点,需要初始化树结点中的各种信息,代码实现如下:
Treenode * Create_node() //创建一个树结点,返回该树结点的指针
{
Treenode * node = new Treenode; //申请一个新的树结点
node->child_num = 0; //初始化树结点的孩子结点数目为 0
node->value = 0; //初始化树结点的代价估计值(启发式)为 0
node->depth = 0; //初始化树结点的深度为 0
node->father = NULL; //初始化树结点的父亲结点(指针)为空
node->child.clear(); //初始化树结点不存在孩子结点(指针)
return node; //返回该树结点的指针
}
宽度优先搜索是简单搜索策略,先扩展根结点,接着扩展根结点的所有后继,然后再扩展它们的后继,依此类推。一般地,在下一层的任何结点扩展之前,搜索树上本层深度的所有结点都应该已经扩展过。
宽度优先搜索是一般图搜索算法的一个实例,每次总是扩展深度最浅的结点。这可以通过将边缘组织成 FIFO 队列来实现。就是说,新结点(结点比其父结点深)加入到队列尾,这意味着浅层的老结点会在深层结点之前被扩展。
宽度优先搜索算法具有一般的图搜索框架,忽视所有到边缘结点或已经扩展结点的新路径,可以看出这样的路径至少和已经找到的一样深,所以,宽度优先搜索总是有到每一个边缘结点的最浅路径。
如果最浅的目标结点处于一个有限深度 d,那么宽度优先搜索是完备的,宽度优先搜索在扩展完比它浅的所有结点之后最终一定能找到该目标结点。但是,最浅的目标结点不一定是最优的目标结点。
时间复杂度:O(b^d )
空间复杂度:O(b^d )
实现 宽度优先搜索算法 的关键数据结构是 队列,具体的流程如下:
void Breadth_First_Search(string start,string goal) //宽度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Breadth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
宽度优先搜索算法解决罗马尼亚问题的搜索路径:
Breadth_First_Search Route :
Arad v : 0 -> Sibiu v : 140 -> Fagaras v : 239 -> Bucharest v : 450 ->
程序运行时间 Run Time :
36.000 ms
当每一步的行动代价都相等时宽度优先搜索是最优的,因为它总是先扩展深度最浅的未扩展结点。更进一步,我们可以找到一个对任何单步代价函数都是最优的算法。不再扩展深度最浅的结点,一致代价搜索扩展的是路径消耗最小的结点。这可以通过将边缘结点集组织成按单步代价值排序的优先队列来实现。
除了按路径代价对队列进行排序外,一致代价搜索和宽度优先搜索有两个显著不同。第一是目标检测应用于结点被选择扩展时,而不是在结点生成的时候进行。理由是第一个生成的目标结点可能在次优的路径上。第二个不同是如果边缘中的结点有更好的路径到达该结点那么会引入一个测试。
时间复杂度:O(b^(1+[C*/ε] ) )
空间复杂度:O(b^(1+[C*/ε] ) )
实现 一致代价搜索算法 的关键数据结构是 优先队列,具体的流程如下:
程序中使用自己写的 sort() 排序实现优先队列的功能。
void Uniform_Cost_Search(string start,string goal) //一致代价搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Uniform_Cost_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
queue<Treenode *> sort(queue<Treenode *> q)
{
queue<Treenode *> s;
map<float,Treenode *> m;
int i=0;
while(!q.empty()){
i++;
Treenode * t = q.front();
q.pop();
if(m[t->value] == NULL)
m[t->value] = t;
else{
m[t->value + i * 0.01] = t;
}
}
map<float,Treenode *>::iterator mit;
for(mit=m.begin();mit!=m.end();mit++)
s.push(mit->second);
return s;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
一致代价搜索算法解决罗马尼亚问题的搜索路径:
Uniform_Cost_Search Route :
Arad v : 0 -> Sibiu v : 140 -> RimnicuVilcea v : 220 -> Pitesti v : 317 -> Bucharest v : 418
程序运行时间 Run Time :
224.000 ms
深度优先搜索总是扩展搜索树的当前边缘结点集中最深的结点。搜索很快推进到搜索树的最深层,那里的结点没有后继。当那些结点扩展完之后,就从边缘结点集中去掉,然后搜索算法回溯到下一个还有未扩展后继的深度稍浅的结点。
宽度优先搜索使用 FIFO 队列,深度优先搜索使用 LIFO 队列。LIFO 队列指的是最新生成的结点最早被选择扩展。这一定是最深的未被扩展的结点,因为它比它的父结点深 1,上一次扩展的则是这个父结点因为它最深。
深度优先搜索算法的效率严重依赖于使用的是图搜索还是树搜索。避免重复状态和冗余路径的图搜索,在有限状态空间是完备的,因为它至多扩展所有结点。而树搜索,则不完备,算法会陷入死循环。同样的原因,无论是基于图搜索还是树搜索的深度优先搜索都不是最优的。深度优先搜索的时间复杂度受限于状态空间的规模。
时间复杂度:O(b^m )
空间复杂度:O(bm)
实现 深度优先搜索算法 的关键数据结构是 栈,具体的流程如下:
void Depth_First_Search(string start,string goal) //深度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
stack<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.top(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<"\tDepth : " <<current->depth <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Depth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
深度优先搜索算法解决罗马尼亚问题的搜索路径:
Depth_First_Search Route :
Arad v : 0 -> Timisoara v : 118 -> Lugoj v : 229 -> Mehadia v : 299 -> Dobreta v : 374 -> Craiova v : 494 -> RimnicuVilcea v : 640 -> Pitesti v : 737 -> Bucharest v : 838 ->
程序运行时间 Run Time :
185.000 ms
迭代加深的深度优先搜索是一种常用策略,它经常和深度优先搜索结合使用来确定最好的深度界限。做法是不断地增大深度限制——首先为 0,接着为 1,然后是 2,以此类推直到找到目标。当深度界限达到 d,即最浅的目标结点所在深度时,就能找到目标结点。
迭代加深的深度优先搜索算法结合了深度优先搜索和宽度优先搜索的优点,它的空间需求是合适的:O(bd)。和宽度优先搜索一样,当分支因子有限时是该搜索算法是完备的,当路径代价是结点深度的非递减函数时该算法是最优的。
也许迭代加深的深度优先搜索看起来比较浪费,因为状态被多次重复生成。但事实上代价并不是多大。原因是在分支因子相同(或者近似)的搜索树中,绝大多数的结点都在底层,所以上层的结点重复生成多次影响不大。
时间复杂度:O(b^d )
如果你确实担忧状态的重复生成,可以混合使用两种搜索算法,先用宽度优先搜索直到有效内存耗尽,然后对边缘集中的所有结点应用迭代加深的深度优先搜索。一般来讲,当搜索空间较大并且不知道解所在的深度时,迭代加深的深度优先搜索是首选的无信息搜索算法。
实现 迭代加深的深度优先搜索算法 的关键数据结构是 栈,具体的流程如下:
int Iterative_Deepening_Depth_First_Search(string start,string goal,int D) //迭代加深的深度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
stack<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.top(); //取出当前需要拓展的城市
Q.pop();
if(current->depth > D) //判断当前结点的深度是否超过限界
continue; //超过限界,跳过该结点,不进行讨论
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<"\tDepth : " <<current->depth <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Iterative_Deepening_Depth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return 1; //搜索结束,返回 1 表示搜索成功
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->depth = current->depth + 1; //子结点的深度为父亲结点的深度加一
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return 0; //返回 0 表示搜索失败,需要继续加深
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
深度界限为 0 : 结果为 Failure
深度界限为 1 : 结果为 Failure
深度界限为 2 : 结果为 Failure
深度界限为 3 :
迭代加深的深度优先搜索算法解决罗马尼亚问题的搜索路径:
Iterative_Deepening_Depth_First_Search Route :
Arad v : 0 -> Sibiu v : 140 -> Fagaras v : 239 -> Bucharest v : 450 ->
程序运行时间 Run Time :
501.000 ms
有信息搜索策略使用问题本身的定义之外的特定知识,比无信息的搜索策略更有效的进行问题求解。一般考虑的算法称为最佳优先搜索。最佳优先搜索中,结点是基于评价函数值被选择扩展的。评价函数被看作是代价评估,因此评估值最低的结点被选择首先进行扩展。最佳优先图搜索的实现与一致代价搜索类似,不过最佳优先是根据评价函数值对优先级队列进行排队。
大多数的最佳优先搜索算法的评价函数是由启发函数构成:
启发函数 = 结点到目标结点的最小代价路径的代价评估值
贪婪最佳优先搜索试图扩展离目标最近的结点,理由是这样可能可以很快找到解。因此,它只用启发式信息。对应到罗马尼亚问题中,使用直线距离启发式:
时间复杂度:O(b^m )
空间复杂度:O(b^m )
实现 贪婪最佳优先搜索算法 的关键数据结构是 优先队列,具体的流程如下:
程序中使用自己写的 sort() 排序实现优先队列的功能。
void Greedy_Best_First_Search(string start,string goal) //贪婪最佳优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
root->value = start_state.h; //根结点的启发函数估计值
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Greedy_Best_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = graph[nextstate_name].h; //子结点的启发函数估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
贪婪最佳优先搜索算法解决罗马尼亚问题的搜索路径:
Greedy_Best_First_Search Route :
Arad v : 366 -> Sibiu v : 253 -> Fagaras v : 176 -> Bucharest v : 0 ->
程序运行时间 Run Time :
101.000 ms
最佳优先搜索的最广为人知的形式称为 A* 搜索。它对结点的评估结合了到达此结点已经花费的代价,和从该结点到目标结点所花代价。由于结合了从开始结点到结点 n 的路径代价和从结点 n 到目标结点的最小代价路径的评估值,因此评估函数值等于经过结点 n 的最小代价解的估计代价。
这样,如果我们想要找到最小代价的解,首先扩展评估函数值最小的结点是合理的。可以发现这个策略不仅仅合理:假设启发式函数满足特定的条件,A* 搜索既是完备的也是最优的。
其中,A* 搜索算法的最优性取决于:如果启发式函数是可采纳的,那么 A* 的树搜索版本是最优的,如果启发式函数是一致的,那么图搜索的 A* 算法是最优的。
实现 A*搜索算法 的关键数据结构是 优先队列,具体的流程如下:
程序中使用自己写的 sort() 排序实现优先队列的功能。
void A_Star_Search(string start,string goal)
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
root->value = start_state.h; //根结点的启发函数估计值
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"A_Star_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
//子结点的启发函数估计值
current_child->value = current->value - current_state.h + current_state.nextstate[i].second + graph[nextstate_name].h;
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
A*搜索算法解决罗马尼亚问题的搜索路径:
A_Star_Search Route :
Arad v : 366 -> Sibiu v : 393 -> RimnicuVilcea v : 413 -> Pitesti v : 417 -> Bucharest v : 418 ->
程序运行时间 Run Time :
103.000 ms
根据我的程序跑出来的结果,在时间上明显是宽度优先搜索最优,宽度优先搜索总是有到每一个边缘结点的最浅路径。因为找到最浅的目标结点,所以最快。
一致代价搜索扩展的是当前路径代价最小的结点,对于一般性的步骤代价而言算法是最优的。
但是宽度优先搜索的实际答案并不是最优的,因为它没有考虑路径代价。并且此次的罗马尼亚问题规模太小,针对大规模的问题搜索时,可以混合使用两种搜索算法,先用宽度优先搜索直到有效内存耗尽,然后对边缘集中的所有结点应用迭代加深的深度优先搜索。一般来讲,当搜索空间较大并且不知道解所在的深度时,迭代加深的深度优先搜索是首选的无信息搜索算法。
通过我的程序跑出来的结果无法准确地判断那种算法最优,因为问题的规模太小了。
考虑到实际问题的搜索求解时,A* 搜索算法是最优的。如果我们想要找到最小代价的解,首先扩展评估函数值最小的结点是合理的。可以发现这个策略不仅仅合理:假设启发式函数满足特定的条件,A* 搜索既是完备的也是最优的。
其中,A* 搜索算法的最优性取决于:如果启发式函数是可采纳的,那么 A* 的树搜索版本是最优的,如果启发式函数是一致的,那么图搜索的 A* 算法是最优的。
无信息搜索指的是除了问题定义中提供的状态信息外没有任何附加信息,只是简单计算达到各个结点所需要的消耗值并比较,有信息搜索策略使用问题本身的定义之外的特定知识,比无信息的搜索策略更有效的进行问题求解。
Search.cpp
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef pair<string,int> neighbor; //当前城市的相邻城市的城市名和对应的路径代价
struct state //罗马尼亚问题中的每一个城市的城市信息
{
string name; //该城市的城市名
int neighbor_num; //与该城市相邻的有几个城市
map<int,neighbor> nextstate; //记录每个相邻的城市和对应的路径代价
int h; //该城市到目标城市的最小代价路径的估计值
};
struct Treenode //搜索树中的树结点定义
{
int child_num; //该树结点有几个孩子结点
int value; //该树结点的代价估计值(启发式)
int depth; //该树结点的深度
state theState; //该树结点对应在哪个城市
Treenode * father; //保存该树结点的父亲结点(指针)
map<int,Treenode *> child; //保存该树结点的所有孩子结点(指针)
};
int state_num = 0; //罗马尼亚问题中的每一个城市
map<string,state> graph; //邻接表保存罗马尼亚问题的图:<城市名,城市信息>
map<string,int> explored; //罗马尼亚问题中的城市是否被拓展过
Treenode * Create_node() //创建一个树结点,返回该树结点的指针
{
Treenode * node = new Treenode; //申请一个新的树结点
node->child_num = 0; //初始化树结点的孩子结点数目为 0
node->value = 0; //初始化树结点的代价估计值(启发式)为 0
node->depth = 0; //初始化树结点的深度为 0
node->father = NULL; //初始化树结点的父亲结点(指针)为空
node->child.clear(); //初始化树结点不存在孩子结点(指针)
return node; //返回该树结点的指针
}
void File_Input(string s)
{
ifstream f(s); //导入描述罗马尼亚问题详细信息的文件
f >>state_num; //输入罗马尼亚问题的总城市数目
for(int i=0;i<state_num;i++){ //初始化每一个城市的城市信息
state t;
f >> t.name; //当前城市的城市名
f >> t.h; //当前城市到目标城市的最小代价路径的估计值
f >> t.neighbor_num; //当前城市有几个相邻的城市
for(int j=0;j<t.neighbor_num;j++){ //保存每一个相邻的城市
f >>t.nextstate[j].first; //保存相邻城市的城市名
f >>t.nextstate[j].second; //保存到相邻城市的路径代价
}
graph[t.name] = t; //在罗马尼亚问题的图中添加当前城市
explored[t.name] = 0; //初始化当前城市未被拓展过
}
}
void Show_Graph()
{
cout <<" State number : " <<state_num <<endl;
map<string,state>::iterator mit;
for(mit=graph.begin();mit!=graph.end();mit++){
cout <<"State : " <<" name : " <<mit->second.name <<endl;
cout <<" " <<" h : " <<mit->second.h <<endl;
for(int j=0; j<mit->second.neighbor_num; j++){
cout <<" " <<" naighbor " <<j <<" : name :" <<mit->second.nextstate[j].first
<<"\tg : " <<mit->second.nextstate[j].second <<endl;
}
}
}
queue<Treenode *> sort(queue<Treenode *> q)
{
queue<Treenode *> s;
map<float,Treenode *> m;
int i=0;
while(!q.empty()){
i++;
Treenode * t = q.front();
q.pop();
if(m[t->value] == NULL)
m[t->value] = t;
else{
m[t->value + i * 0.01] = t;
}
}
map<float,Treenode *>::iterator mit;
for(mit=m.begin();mit!=m.end();mit++)
s.push(mit->second);
return s;
}
void Route(Treenode * c,string s) //递归得到搜索路径
{
if(c->theState.name == s){ //当前城市为目标城市
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
Route(c->father,s); //当前城市还不是目标城市,继续递归
//输出当前城市和代价估计值(启发式)
cout <<c->theState.name <<" v : "<<c->value <<" -> ";
return;
}
void Breadth_First_Search(string start,string goal) //宽度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Breadth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Uniform_Cost_Search(string start,string goal) //一致代价搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Uniform_Cost_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void Depth_First_Search(string start,string goal) //深度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
stack<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.top(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<"\tDepth : " <<current->depth <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Depth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
int Iterative_Deepening_Depth_First_Search(string start,string goal,int D) //迭代加深的深度优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
stack<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.top(); //取出当前需要拓展的城市
Q.pop();
if(current->depth > D) //判断当前结点的深度是否超过限界
continue; //超过限界,跳过该结点,不进行讨论
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<"\tDepth : " <<current->depth <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Iterative_Deepening_Depth_First_Search Route : ";
Route(current,start); //输出搜索的路径
return 1; //搜索结束,返回 1 表示搜索成功
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = current->value + current_state.nextstate[i].second; //子结点的代价估计值
current_child->depth = current->depth + 1; //子结点的深度为父亲结点的深度加一
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return 0; //返回 0 表示搜索失败,需要继续加深
}
void Greedy_Best_First_Search(string start,string goal) //贪婪最佳优先搜索算法
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
root->value = start_state.h; //根结点的启发函数估计值
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"Greedy_Best_First_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
current_child->value = graph[nextstate_name].h; //子结点的启发函数估计值
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
void A_Star_Search(string start,string goal)
{
state start_state = graph[start]; //获取开始城市的城市信息
Treenode *root = Create_node(); //创建搜索树一个根结点
queue<Treenode *> Q; //记录保存待拓展的城市集合
root->theState = start_state; //根结点的城市为开始城市
root->value = start_state.h; //根结点的启发函数估计值
Q.push(root); //将开始城市加入待拓展的城市集合(根结点)
while(!Q.empty()){ //还存在待拓展的城市
Treenode * current = Q.front(); //取出当前需要拓展的城市
Q.pop();
state current_state = current->theState; //获取当前城市的城市信息
cout <<"State name : " <<current_state.name <<"\tValue : " <<current->value <<endl;
explored[current_state.name] = 1; //将当前城市设置为已经拓展过
if(current_state.name == goal){ //判断当前城市是不是目标城市
cout <<endl <<endl <<"A_Star_Search Route : ";
Route(current,start); //输出搜索的路径
return; //搜索结束,返回
}
for(int i=0;i<current_state.neighbor_num;i++){ //拓展当前城市的所有相邻城市
string nextstate_name = current_state.nextstate[i].first; //获取相邻城市的城市名
Treenode * current_child = Create_node(); //创建一个新的子结点
current_child->theState = graph[nextstate_name]; //子结点的城市信息
//子结点的启发函数估计值
current_child->value = current->value - current_state.h + current_state.nextstate[i].second + graph[nextstate_name].h;
current_child->father = current; //子结点的父亲结点是当前正在拓展的结点
current->child[current->child_num] = current_child; //在当前正在拓展的结点中加入该子结点
current->child_num++; //当前正在拓展的结点中孩子结点数加一
cout <<"\tChild State : " <<nextstate_name <<"\tValue : " <<current_child->value;
if(explored[nextstate_name] == 0){ //判断该相邻城市是否已经被拓展过
Q.push(current_child); //没被拓展过,加入待拓展的城市集合(树结点)
}
}
Q = sort(Q); //进行优先队列的排序,实现优先队列的功能
cout <<endl<<endl;
}
cout <<"Failure" <<endl; //找不到可以到目标城市的路径,搜索失败
return;
}
int main()
{
// 记录程序运行时间
clock_t start,end,over;
start=clock();
end=clock();
over=end-start;
ifstream file("Select_Search.txt");
string select_Search;
while(cout <<endl <<endl <<"Choose How to Search : " && file >>select_Search){
File_Input("Romania.txt");
// Show_Graph();
start=clock();
if(select_Search == "BFS"){
cout <<endl <<"Breadth_First_Search" <<endl;
Breadth_First_Search("Arad","Bucharest");
}
else if(select_Search == "UCS"){
cout <<endl <<"Uniform_Cost_Search" <<endl;
Uniform_Cost_Search("Arad","Bucharest");
}
else if(select_Search == "DFS"){
cout <<endl <<"Depth_First_Search" <<endl;
Depth_First_Search("Arad","Bucharest");
}
else if(select_Search == "IDDFS"){
for(int i=0; ;i++){
File_Input("Romania.txt");
cout <<endl <<"Iterative_Deepening_Depth_First_Search : Limit : " <<i <<endl;
int ans = Iterative_Deepening_Depth_First_Search("Arad","Bucharest",i);
if(ans == 1)
break;
}
}
else if(select_Search == "GBFS"){
cout <<endl <<"Greedy_Best_First_Search" <<endl;
Greedy_Best_First_Search("Arad","Bucharest");
}
else if(select_Search == "ASS"){
cout <<endl <<"A_Star_Search" <<endl;
A_Star_Search("Arad","Bucharest");
}
else{
break;
}
end=clock();
printf("\nRun Time : %6.3f ms\n",(double)(end-start-over)/CLOCKS_PER_SEC*1000);
}
}
Romania.txt
20
Oradea 380 2 Zerind 71 Sibiu 151
Zerind 374 2 Oradea 71 Arad 75
Arad 366 3 Zerind 75 Sibiu 140 Timisoara 118
Sibiu 253 4 Oradea 151 Arad 140 Fagaras 99 RimnicuVilcea 80
Timisoara 329 2 Arad 118 Lugoj 111
Lugoj 244 2 Timisoara 111 Mehadia 70
Mehadia 241 2 Lugoj 70 Dobreta 75
Dobreta 242 2 Mehadia 75 Craiova 120
Fagaras 176 2 Sibiu 99 Bucharest 211
RimnicuVilcea 193 3 Sibiu 80 Craiova 146 Pitesti 97
Craiova 160 3 Dobreta 120 Pitesti 138 RimnicuVilcea 146
Pitesti 100 3 Craiova 138 Bucharest 101 RimnicuVilcea 97
Bucharest 0 4 Fagaras 211 Pitesti 101 Giurgiu 90 Urziceni 85
Giurgiu 77 1 Bucharest 90
Urziceni 80 3 Bucharest 85 Hirsova 98 Vaslui 142
Hirsova 151 2 Urziceni 98 Eforie 86
Eforie 161 1 Hirsova 86
Vaslui 199 2 Urziceni 142 Iasi 92
Iasi 226 2 Vaslui 92 Neamt 87
Neamt 234 1 Lasi 87
Select_Search.txt
BFS
UCS
DFS
IDDFS
GBFS
ASS
END