zoukankan      html  css  js  c++  java
  • 人工智能搜索算法(深度优先、迭代加深、一致代价、A*搜索)

    搜索算法问题求解

    一、需求分析

    分别用深度优先、迭代加深、一致代价、A*搜索算法得到从起始点Arad到目标点Bucharest的一条路径。即为罗马尼亚问题的一个解,在求解的过程中记录每种算法得到的解。即输出每种解得到的条路径。


    图一:罗马尼亚地图 

    二、具体代码

    測试类:

    /**Main类,打印各个算法的结果
    * @author dyl
    *
    */
    classMain{
    int result;
    int xiabiao[]=null;//訪问的下标
    publicstaticvoid main(String[] args){
    Graph graph=newGraph();
    System.out.println("----------------罗马尼亚问题---------------");
    System.out.println("1、深度优先搜索");
    DFS dfs=new DFS();
    dfs.DF_Search(graph,0,12);
    System.out.println("2、迭代加深的搜索");
    IDS ids=new IDS();
    ids.IDS_Search(graph,0,12,15);//深度设15
    System.out.println("3、一致代价搜索");
    UCS ucs=new UCS(graph,0,12);
    System.out.println("4、A*搜索");
    AXing aXing=newAXing();
    aXing.A_Search(graph, graph.H,0,15);//0-15即Arad到达Hirsova
    }
    /**打印
    * @param g:图
    * @param stack:栈
    */
    publicvoid show(Graph g,Stack stack){
    if(stack.size()==0){
    System.out.println("路径搜索失败");
    return;
    }
    result=0;
    System.out.print("訪问的下标: ");
    for(int i =0; i < stack.size(); i++){
    System.out.print("-->"+stack.get(i));
    }
    System.out.print(" 訪问过程: ");
    xiabiao=newint[stack.size()];
    if(stack.isEmpty()){
    System.out.println("搜索失败");
    }else{
    for(int i =0; i < stack.size(); i++){
    System.out.print("-->"+g.cities[(Integer) stack.get(i)]);
    }
    for(int i =0; i < stack.size()-1; i++){
    result+=g.path[(Integer) stack.get(i)][(Integer) stack.get(i+1)];
    }
    System.out.println(" 总长度为:"+result+" ");
    g.markInit();//清空訪问
    }
    }
    }
    /**图类
    * @author dyl
    *
    */
    publicclassGraph{
    publicint path[][]=newint[][]{{0,75,10000,118,140,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {75,0,71,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,71,0,10000,151,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {118,10000,10000,0,10000,111,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {140,10000,151,10000,0,10000,80,99,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,111,10000,0,10000,10000,70,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,80,10000,0,10000,10000,10000,146,97,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,99,10000,10000,0,10000,10000,10000,10000,211,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,70,10000,10000,0,75,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,75,0,120,10000,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,146,10000,10000,120,0,138,10000,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,97,10000,10000,10000,138,0,101,10000,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,211,10000,10000,10000,101,0,90,85,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,90,0,10000,10000,10000,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,85,10000,0,98,10000,142,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,98,0,86,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,86,0,10000,10000,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,142,10000,10000,0,92,10000},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,92,0,87},
    {10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,87,0}};
    publicint[]H=newint[]{516,524,530,479,403,394,343,326,391,392,310,160,150,155,100,0};//启示式函数
    publicString[] cities=newString[]{"Arad","Zerind","Oradea","Timisoara","Sibiu","Lugoj",
    "Rimnicu Vilcea","Fagaras","Mehadia","Drobeta","Craiova","Pitesti","Bucharest","Giurgiu","Urziceni","Hirsova",
    "Eforie","Vaslui","Isi","Neamt"};//城市名
    publicint[]mark=newint[20];//訪问标记
    publicGraph(){//得到数据
    markInit();
    }
     
    /**
    * 訪问标志初始化
    */
    publicvoid markInit(){
    for(int i =0; i < mark.length; i++){
    mark[i]=0;
    }
    }
    /**第一个孩子
    * @param g
    * @param start
    * @return -1表示一个孩子都没有
    */
    publicint getFirstVex(int start){
    if(start>=0&&start<path.length){
    for(int j =0; j < path.length; j++)
    if(path[start][j]<10000&&path[start][j]>0)//有关系
    return j;
    }
    return-1;
    }
     
    /**下一个孩子
    * @param start
    * @param w
    * @return 表示图G中顶点i的第j个邻接顶点的下一个邻接顶点
    * 返回-1,表示后面没有邻接点了
    */
    publicint getNextVex(int start,int w){
    if(start>=0&&start<path.length&&w>=0&&w<path.length){
    for(int i = w+1; i < path.length; i++)
    if(path[start][i]<10000&&path[start][i]>0)
    return i;
    }
    return-1;
    }
    publicint getNumber(){
    return path.length;
    }
    }
     
    l 【深度优先】

    基本原理:深度优先搜索採用堆栈寻找路径,首先从Arad结点出发,推断是否为目标结点,若否,寻找与该结点的邻接点,先搜索一条分支上的全部节点。然后再去搜索和Arad的其他分支结点,找出并存进待扩展结点表。等待扩展,每次先推断待扩展结点表是否为空,若否,则从待扩展结点表中取出一个结点进行扩展,并将扩展后的结点存进该表,若是,则返回失败。

    深度优先搜索类:

    /**深度优先搜索类
    * @author dyl
    *
    */
    publicclass DFS {
    Stack stack=newStack<Integer>();
    int x;
    int w;//v0的第一个邻接点
    /**深度优先搜索--非递归式
    * @param g :图
    * @param v0:開始节点
    * @param vg:终于节点
    */
    publicvoid DF_Search(Graph g,int v0,int vg){
    stack.push(v0);//入栈
    g.mark[v0]=1;//v0被訪问
    while(true){
    x=(Integer) stack.peek();//查看栈顶元素
    w=g.getFirstVex(x);
    while(g.mark[w]==1){//被訪问,则寻找下一个邻接点
    w=g.getNextVex(x, w);
    if(w==-1){
    break;
    }
    }
    while(w==-1){//没有找到下一个邻接点
    stack.pop();
    x=(Integer) stack.peek();
    w=g.getFirstVex(x);
    while(g.mark[w]==1){
    w=g.getNextVex(x, w);
    if(w==-1){
    break;
    }
    }
    }
    stack.push(w);
    g.mark[w]=1;
    if(w==vg)break;//到达终点
    }
    newMain().show(g, stack);
    }
    }
    实验结果:

     

    实验分析:

    依据结果可仅仅知。在有限状态空间下,树搜索不是完备的。图搜索完备。无限状态下不完备。

    此结果0->1->2->4->6->10->11->12仅仅是当中一条,但不是最优解。

    分支因子b,深度d

    则最坏情况下时间复杂度也高达,空间复杂度  ,内存需求少。


    l 【迭代加深】

    基本原理:

    迭代加深搜索是以DFS为基础的,它限制DFS递归的层数。

    迭代加深搜索的基本步骤是:

    1、设置一个固定的深度depth。一般是depth = 1,即仅仅搜索初始状态

    2、DFS进行搜索。限制层数为depth。假设找到答案,则结束,假设没有找到答案 则继续下一步

    3、假设DFS途中遇到过更深的层,则++depth。并反复2。假设没有遇到。说明搜 索已经结束。没有答案

    /**迭代加深
    * @author dyl
    */
    publicclass IDS {
    Stack stack=newStack<Integer>();
    /**迭代加深搜索
    * @param g:图
    * @param v0:開始节点
    * @param vg:目的节点
    * @param depthMax:depthMax
    */
    publicvoid IDS_Search(Graph g,int v0,int vg,int depthMax){
    for(int i =2; i <=depthMax; i++){//迭代depthMax次
    if(dfsearch(g, v0, vg,i)==1){
    break;
    }
    }
    }
    /**深度搜索
    * @param g:图
    * @param v0:開始节点
    * @param vg:目的节点
    * @param depthMax:depthMax
    * @return
    */
    publicint dfsearch(Graph g,int v0,int vg,int depthMax){
    int x;
    int w;//v0的第一个邻接点
    stack.push(v0);//入栈
    g.mark[v0]=1;//v0被訪问
    while(true){
    x=(Integer) stack.peek();//查看栈顶元素
    w=g.getFirstVex(x);
    while(g.mark[w]==1){//被訪问,则寻找下一个邻接点
    w=g.getNextVex(x, w);
    if(w==-1){
    break;
    }
    }
    while(w==-1){//没有找到下一个邻接点
    stack.pop();
    if(stack.size()==0){//清空了栈里的元素
    g.markInit();//訪问初始化
    return0;
    }
    x=(Integer) stack.peek();
    w=g.getFirstVex(x);
    while(g.mark[w]==1){
    w=g.getNextVex(x, w);
    if(w==-1){
    break;
    }
    }
    }
    stack.push(w);
    g.mark[w]=1;
    if(w==vg)
    {
    break;
    }//检查是否达到终点
    if(stack.size()>=depthMax){//又一次迭代则又一次初始化值
    stack.pop();
    }
    }
    newMain().show(g, stack);
    return1;
    }
    }

    实验结果:

     

    实验分析:

             由于迭代加深是从依照深度的递增搜索的,所以说0-》1-》2-》4-》7-》12这条 路径。仅仅是在深度最低的情况下找到的结果,并非最优解。

    是完备的,时间复杂度也 高达,空间复杂度。

    l 【一致代价】

    基本原理:

    扩展的是路径消耗g(n)最小的节点n,用优先队列来实现。对解的路径步数不关心,仅仅关心路径总代价。

    即使找到目标节点也不会结束,而是再检查新路径是不是要比老路径好,确实好。则丢弃老路径。

    /**
    * 一致代价类
    *
    */
    publicclass UCS {
     
    public UCS(Graph g,int start,int end){
    int[] pre =newint[20];// 保存各个结点的前驱结点
    int[] dist =newint[20];// 用于保存当前结点到起始结点的实际路径长度
    for(int i =0; i < pre.length; i++)
    {
    pre[i]=-1;
    dist[i]=10000;
    }
    // 调用一致搜索算法搜索路径
    UC_search(g,start, end, dist, pre);
    // 打印路径显示函数
    displayPath(start, end, pre,g);
    }
    /**
    * @param start:開始
    * @param goal:目的
    * @param prev:前驱节点
    * @param g:图
    */
    publicvoid displayPath(int start,int goal,int[] prev,Graph g)
    {
    Stack<Integer> stack =newStack<Integer>();
    stack.push(goal);
    while(prev[goal]!= start)
    {
    stack.push(prev[goal]);
    goal = prev[goal];
    }
    stack.push(start);
    System.out.print("訪问的下标: ");
    for(int i = stack.size()-1; i >=0; i--){
    System.out.print("-->"+stack.get(i));
    }
    System.out.print(" 訪问过程: ");
    for(int i = stack.size()-1; i >=0; i--){
    System.out.print("-->"+ g.cities[stack.get(i)]);
    }
    System.out.print(" 总长度为: ");
    int result=0;
    for(int i =0; i < stack.size()-1; i++){
    result+=g.path[stack.get(i)][stack.get(i+1)];
    }
    System.out.print(result);
    System.out.println(" ");
    g.markInit();
    }
    /**
    * @param g:图
    * @param start:開始
    * @param goal:目的
    * @param prev:前驱节点
    *
    */
    publicvoid UC_search(Graph g,int start,int goal,int[] dist,int[] pre)
    {
    List<Integer> list =newArrayList<Integer>();
    list.add(start);
    while(!list.isEmpty())
    {
    moveMinToTop(list, dist);// 将dist数组中最小值所相应的结点。移至list队首
    int current = list.remove(0);// 将list队首的结点出队。并展开
    g.mark[current]=1;
    if(current == goal)
    {
    return;
    }
     
    for(int j =0; j < g.path[current].length; j++)
    {
    if(g.path[current][j]<10000&& g.mark[j]==0)
    {
    if(!isInList(j, list))// 结点j不在队列里
    {
    list.add(j);
    pre[j]= current;
    dist[j]= dist[current]+ g.path[current][j];
    }
    elseif((dist[current]+ g.path[current][j])< dist[j])
    {
    pre[j]= current;
    dist[j]= dist[current]+ g.path[current][j];
    }
    }
    }
    if(list.isEmpty())
    {
    System.out.println("搜索不成功!");
    }
    }
    }
    /**
    * 检查结点a,是否在队列list里
    */
    publicboolean isInList(int a,List<Integer> list)
    {
    for(int i =0; i < list.size(); i++)
    {
    if(list.get(i)== a)
    {
    returntrue;
    }
    }
    returnfalse;
    }
    /**
    * 将dist数组中的最小值所相应的结点。从list队列中移至队列头
    */
    publicvoid moveMinToTop(List<Integer> list,int[] dist)
    {
    int index =0;
    int min = dist[index];
    for(int i =0; i < list.size(); i++)
    {
    int a = list.get(i);
    if(dist[a]< min)
    {
    index = i;
    min = dist[a];
    }
    }
    int temp = list.get(index);
    for(int i = index; i >0; i--)
    {
    list.set(i, list.get(i -1));
    }
    list.set(0, temp);
    }
    }
    实验结果:

     

    实验分析:

    从结果0-》4-》6-》11-》12能够看出。是最优解,他的复杂度不能简单地使用b、d刻画。得使用C*表示最优解的耗散值。时间复杂度,空间复杂度。

      

    l 【A*搜索】

    基本原理:

    公式表示为: f(n)=g(n)+h(n),

    当中 f(n) 是从初始点经由节点n到目标点的估价函数。

    g(n) 是在状态空间中从初始节点到n节点的实际代价,

    h(n) 是从n到目标节点最佳路径的预计代价。

    保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取:

    首先将起始结点S放入OPEN表,CLOSE表置空,算法開始时:      

    1、假设OPEN表不为空,从表头取一个结点n,假设为空算法失败。      

    2、n是目标解吗?是,找到一个解(继续寻找。或终止算法)。      

    3、将n的全部后继结点展开,就是从n能够直接关联的结点(子结点),假设不在CLOSE表中,就将它们放入OPEN表,并把S放入CLOSE表,同一时候计算每个后继结点的估价值f(n),将OPEN表按f(x)排序,最小的放在表头,反复算法,回到1。

    import java.util.Stack;
     
    /** A*搜索类
    * @author dyl
    *
    */
    publicclassAXing{
    intMaxWeight=10000;//表示无穷大
    Stack stack=newStack<Integer>();
    /**A*搜索
    * @param g:图
    * @param H:启示式函数值
    * @param v0:初始值
    * @param end:目标值
    */
    publicvoid A_Search(Graph g,int H[],int v0,int end){
    boolean flag=true;
    int x;//表示栈顶元素
    int vex;//寻找目标节点
    intMinF,MinVex= v0;//记录最小的f(n)和相应的节点
    int[][]GHF=newint[g.path.length][3];//分别用于存储g(n),h(n),f(n)
    for(int i =0; i < g.path.length; i++){
    GHF[i][0]=0;
    GHF[i][2]=MaxWeight;//对f(n)初始化,1000表示无穷大
    }
    stack.push(v0);//v0入栈
    GHF[v0][0]=0;//g(n)
    GHF[v0][1]=H[v0];//h(n)
    GHF[v0][2]=GHF[v0][0]+GHF[v0][1];//f(n)
    g.mark[v0]=1;
    while(flag){
    MinF=MaxWeight;
    x=(Integer) stack.peek();
    //处理第一个子节点
    vex=g.getFirstVex(x);
    if(vex==end){//找到目标节点
    stack.push(vex);
    g.mark[vex]=1;
    break;
    }
    if(vex!=-1){//子节点能找到。继续
    if(g.mark[vex]==0){//没被訪问
    GHF[vex][0]=GHF[x][0]+g.path[x][vex];//节点vex的g(n)
    GHF[vex][1]=H[vex];//节点vex的h(n)
    GHF[vex][2]=GHF[vex][0]+GHF[vex][1];
    if(GHF[vex][2]<MinF){
    MinF=GHF[vex][2];
    MinVex=vex;
    }
    }
    //处理剩下的邻接点(宽度遍历)
    while(vex!=-1){
    vex=g.getNextVex(x, vex);
    if(vex!=-1&&g.mark[vex]==0){//有邻节点
    GHF[vex][0]=GHF[x][0]+g.path[x][vex];//节点vex的g(n)
    GHF[vex][1]=H[vex];//节点vex的h(n)
    GHF[vex][2]=GHF[vex][0]+GHF[vex][1];
    if(GHF[vex][2]<MinF){
    MinF=GHF[vex][2];
    MinVex=vex;
    }
    }
    if(vex==-1){//没有邻接点了,此时确定最小消耗节点,并压栈
    stack.push(MinVex);
    g.mark[MinVex]=1;
    break;
    }
    if(vex==end){
    stack.push(vex);//压栈目标节点
    g.mark[vex]=1;
    flag=false;
    break;
    }
    }
    }
    else{//没有子节点或者子节点被訪问了,循环出栈
    while(vex==-1){
    stack.pop();
    }
    }
    }
    newMain().show(g, stack);
    }
    }

    实验结果:

     

     实验分析:

    A*搜索估价值h(n)<= n到目标节点的距离实际值,这样的情况下,搜索的点数多。搜索范围大,效率低。

    但能得到最优解。而且假设h(n)=d(n),即距离预计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。假设 估价值>实际值,搜索的点数少,搜索范围小,效率高。但不能保证得到最优解

     

    三、实验结果:

     

    四、思考题

    1、依据实验结果分析深度优先搜索。一致代价搜索,迭代加深的深度优先搜索算法的时间和空间复杂度。


    2、依据实验结果分析A*搜索的性能。 

    答:A*算法是一种静态路网中求解最短路径最有效的直接搜索方法。

    估价值与实际值越接近。估价函数取得就越好。

    0-4-6-11-12-14-15从图中能够看出是最优解,估价值h(n)<= n到目标节点的距离实际值。这样的情况下,搜索的点数多,搜索范围大。效率低。但能得到最优解。而且假设h(n)=d(n),即距离预计h(n)等于最短距离。那么搜索将严格沿着最短路径进行。 此时的搜索效率是最高的。假设 估价值>实际值,搜索的点数少,搜索范围小。效率高。但不能保证得到最优解

        

    该实验是人工智能的实验。之前做的时候没考虑到代码优化。所以代码量有点大。请大家见谅。如有不正确的地方,希望大家提出建议。


  • 相关阅读:
    78. Subsets
    93. Restore IP Addresses
    71. Simplify Path
    82. Remove Duplicates from Sorted List II
    95. Unique Binary Search Trees II
    96. Unique Binary Search Trees
    312. Burst Balloons
    程序员社交平台
    APP Store开发指南
    iOS框架搭建(MVC,自定义TabBar)--微博搭建为例
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7249210.html
Copyright © 2011-2022 走看看