精华内容
下载资源
问答
  • 初学A*算法求解静态地图的最短路径

    千次阅读 2015-12-07 04:13:08
    以前所接触过的最短路径算法是dijkstra或floyd之类的,都是在已知每两点之间距离的情况下求最短路的。那么想一下这样的案例 给你一个地图,类似于迷宫一样,中间有些障碍物,再给定起点终点,求该两点间最短路,...

    以前所接触过的最短路径算法是dijkstra或floyd之类的,都是在已知每两点之间距离的情况下求最短路的。

    那么想一下这样的案例

    给你一个地图,类似于迷宫一样,中间有些障碍物,再给定起点终点,求该两点间最短路,显然,上述两种算法就不适用了,因为提到的迷宫,我们可能会很容易想到广搜bfs,但一次bfs下来实际上求出了起点到所有点的最短路径,但我们只想知道它与终点间的最短路径,也就是说这个方案里有很多冗余的操作。

    说到这里,该步入正题,介绍A*算法。

    A*算法;A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法。估价值与实际值越接近,估价函数取得就越好。(取自百度百科)

    何谓直接搜索方法,即不进行任何预处理直接进行搜索的方法
    那么它为何可以称为最有效的直接搜索方法呢,我们看到上文说到一个估价值与估价函数。
    所以有这么一个式子

    f(n) = g(n) + h(n)

    先记住这个式子,在讨论这个式子之前,先谈点题外话。
    我们都接触过贪心思想,即在对问题求解时,总是做出在当前看来是最好的选择。
    从起点开始,它可走的地方是周围的8个点(最多),平常广搜时多个可能是平等对待,全部考虑。

    那么?我们可不可以运用贪心的思想从8个点中选出一个最有可能达到最短路的点来优先进行操作呢?

    当然可以,这样我们就是有针对性的搜索,而不是盲目搜索了。

    有的道友可能会说了,这样贪心下来求出的只能是接近于最优解的相对最优解,并不能保证一定最优,没错,事实的确是这样。
    那么这样就行不通了,因为我们的目的是很明确的,要的是最短路径。

    但是往常的贪心算法都是选取当前状态下最好的选择,至于次好选择等等全部就丢掉了,那如果我们仍然保留这些选择,仍与后来的可能进行比较呢,这样就保证最后的解一定是最优解了。

    如何将所有可能性都保留下来继续参与后来的比较呢,优先队列是一个不错的法子。
    1. 从A开始,将其周围可走且未标记的点,全部加入队列,然后标记A点
    2. 从队列中选取优先度最高的点B
    3. 对B重复1操作,直到到达终点为止

    但优先度如果获得呢,现在我们回到这个式子

    f(n) = g(n) + h(n)
    g(n)为从起点到点n所花费的代价值
    h(n)为点n到终点所花费代价的估价值
    两者之和 f(n)就是点n的优先值

    例如,对几何地图进行求解时,可以将从起点到点n走的步数作为g(n),将点n到终点的几何距离作为h(n)估价值,那么此时,f(n)的值越小,优先度越高。

    下面贴代码

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<math.h>
    #include<queue>
    #include<functional>
    #include<set>
    
    using namespace std;
    
    const int N = 10009;//地图范围
    
    int v[N][N] = {};//记录地图
    int dx[] = {1,1,1,0,0,-1,-1,-1};//8个方向
    int dy[] = {1,0,-1,1,-1,1,-1,0};
    int f[N*N];//对当前点进行标记,同时记录上一个位置的坐标
    
    struct Node
    {
        int x,y,f,g;//坐标与权值
    };
    
    struct Nodecmp//优先队列比较函数
    {
        bool operator() (const Node &a, const Node &b)
        {
            return a.f > b.f;
        }
    };
    
    int leng(Node a, Node b)//求点与终点的几何长度
    {
        return sqrt((a.x - b.x)*(a.x - b.x)
                    + (a.y - b.y)*(a.y - b.y));
    }
    
    void print(int nid, int n)//输出最短路径
    {
        if(nid == -1)
            return;
        print(f[nid],n);
        printf("%d %d\n",(nid-1)/n, nid-(nid-1)/n*n);
    }
    
    bool a_start(Node s, Node e, int n)
    {
        if(!v[e.x][e.y])//终点不可达
            return false;
        memset(f, 0, sizeof(f));
        priority_queue<Node, vector<Node>, Nodecmp> q;
        int eid = e.x * n + e.y;
        f[s.x * n + s.y] = -1;//二维转一维进行标记,便于操作
        while(!q.empty())
        {
            Node now = q.top();
            q.pop();//出队
            int nid = now.x * n + now.y;
            if(nid == eid)//判断是否为终点
            {
                print(nid, n);
                return true;
            }
            for(int i=0;i<8;i++)//遍历8个方向
            {
                Node t;
                t.x = dx[i] + now.x;
                t.y = dy[i] + now.y;
                int tid = t.x * n + t.y;
                if(v[t.x][t.y] && f[tid] == 0)
                {//符合条件便标记并入队
                    f[tid] = nid;
                    t.g = now.g + 1;
                    t.f = t.g + leng(t, e);
                    q.push(t);
                }
            }
        }
        return false;
    }
    
    int main()
    {
        int n;
        Node s,e;
        s.g = 0;
        cin>>n>>s.x>>s.y>>e.x>>e.y;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                cin>>v[i][j];
        if(!a_start(s,e,n))
            cout<<"无法到达"<<endl;
    }
    展开全文
  • A*算法求解迷宫寻路问题实验内容设置相关数据设置两种地图设置两种启发式函数性能分析分析不同起点终点分析不同启发式函数分析不同地图总结附录代码 实验内容 在一个n×m的迷宫里,入口坐标和出口坐标分别为(1,1)和...

    A*算法求解迷宫寻路问题资源链接如右:A*算法求解迷宫寻路问题

    实验内容

    在一个n×m的迷宫里,入口坐标和出口坐标分别为(startx,starty)和(endx,eny),每一个坐标点有两种可能:0或1,其中0表示该位置允许通过,1表示该位置不允许通过。以寻路问题为例实现A*算法的求解程序,设计两种不同的估价函数。

    设置相关数据

    设置两种地图

    根据题意,用矩阵设置两个地图。
    地图1:设置5行5列的迷宫,代码如下:

    a = np.mat([[0, 0, 0, 0, 0], 
                [1, 0, 1, 0, 1], 
                [0, 0, 1, 1, 1], 
                [0, 1, 0, 0, 0], 
                [0, 0, 0, 1, 0]])
    

    地图2:设置20行20列的迷宫,代码如下

    a = np.mat([[0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1], 
                [0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1], 
                [0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1],
                [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1],
                [1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1],
                [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1],
                [0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1],
                [1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1],
                [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1],
                [0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
                [0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1],
                [0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1],
                [0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1],
                [1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
                [0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
                [0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
                [1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0],
                [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0]])
    
    

    设置两种启发式函数

    定义估价函数
    f(n)=g(n)+h(n)f(n)=g(n)+h(n)
    式中,g(n)g(n)为起点到nn状态的最短路径代价的估计值,h(n)h(n)nn状态到目的状态的最短路径代价的估计值。
    g(n)g(n)为起点到nn状态的曼哈顿距离,代码如下:

    def gs(i, j):
        return abs(i - startx) + abs(j - starty)
    

    定义两种启发式函数。
    启发式函数h1(n)h_1 (n)h1(n)=10×(nxendx+nyendy)h_1 (n)=10×(|n_x-endx|+|n_y-endy|),代码如下:

    def h1(i, j):
        return 10*(abs(i - endx) + abs(j - endy))
    

    启发式函数h2(n)h_2 (n)h2(n)=(nxendx)2+(nyendy)2h_2 (n)=(n_x-endx)^2+(n_y-endy)^2,代码如下:

    def h2(i, j):
        return pow(i - endx, 2) + pow(j - endy, 2)
    

    性能分析

    分析不同起点终点

    采用地图2,具体如下图1,启发式函数h1h_1进行分析,分别设置起点为(0,0)和(9,5),终点为(20,20)、(18,20)。
    图1 地图

    (0,0)→(20,20)的路径如图2,(9,5)→(18,20)的路径如图3。
    图2 路径1
    图3 路径2
    两次寻路过程的性能分析如下表1。

    路径 (0,0)→(20,20) (9,5)→(18,20)
    扩展节点数 107 80
    生成节点数 79 59
    运行时间 0.001601705298526 0.000871252588694

    由上述图表可知路径延长后运行时间会相对变长,扩展节点及生成节点也会相应增多,且两次寻找的路程有重合的部分,所以寻找到的路径为较优路径。

    分析不同启发式函数

    采用地图2,启发式函数h1h2h_1,h_2进行分析,设置起点为(0,0),终点为(20,20),两次寻路过程的路径,扩展节点数、生成节点数和运行时间分别如下图4,图5。
    图4 启发式函数h1
    图4 启发式函数h2

    由图表可知,两种启发式函数所用时间相近,但h2h_2相比h1h_1所扩展的节点数和生成的节点数会减少,仔细分析可以发现启发式函数h2h_2陷入的凹路径会少一些,更接近最优路径,但在远离目标点的路径行进时还是会陷进凹路径。

    分析不同地图

    上述过程一直采用地图2进行实验,为验证程序的普适性,现采用地图1进行实验。经过分析,选择上述较优的启发式函数h2h_2进行分析。寻路问题初始节点为(0,0),目标节点为(5,5),寻路路径如图5。
    图5 地图1
    应用启发式函数h2h_2对地图2的寻路路径彩绘图如下(因显示问题,第一行略去)。
    图6 地图2

    汇总上述性能报告如下表2。

    地图 启发式函数 扩展节点数 生成节点数 运行时间
    地图1 h2h_2 14 13 0.0001113599
    地图2 h1h_1 107 79 0.0008827725
    地图2 h2h_2 103 71 0.0007970126

    总结

    A*算法在地图寻路问题中具有很好的优势,相比宽度优先搜索,效率更高,所耗时间更少,相比深度优先搜索,可以解决其不能找到最优解的不足,具有较高的应用价值。该算法的重点及难点就是启发式函数的选择,通过上述实验,可以发现启发式函数h2h_2相比h1h_1效果更好,但仍存在一些问题,需要继续找寻更优的启发式函数。

    附录代码

    主程序代码如下,其中用到的方法和地图在上述代码中已定义,故不再给出。设置不同的起点与终点需相应的更改startx,starty和endx,endy的值。

    b = a
    startx, starty = 1, 1
    i = startx - 1
    j = starty - 1
    endx, endy = 20, 20
    Open, Close = [[i + 1, j + 1]], [[i + 1, j + 1]]
    Opens = [[i + 1, j + 1]]
    road = 100
    start = time.clock()
    while(Open != []):  
        if(Open[-1] != [endx, endy]):
            Open = []
            minf = 10000
            if(i + 1 < len(a) and a[i + 1, j] == 0):
                f = gs((i + 1), j) + h2((i + 1), j)
                if([i + 2, j + 1] not in Close):
                    Open.append([i + 2, j + 1])
                if(f < minf):
                    minf = f
                    i1 = i + 1
                    j1 = j
            if(i - 1 >= 0 and a[i - 1, j] == 0):
                f = gs((i - 1), j) + h2((i - 1), j)
                if([i, j + 1] not in Close):
                    Open.append([i, j + 1])
                if(f < minf):
                    minf = f
                    i1 = i - 1
                    j1 = j
            if(j + 1 < len(a) and a[i, j + 1] == 0):
                f = gs(i, (j + 1)) + h2(i, (j + 1))
                if([i + 1, j + 2] not in Close):
                    Open.append([i + 1, j + 2])
                if(f < minf):
                    minf = f
                    i1 = i
                    j1 = j + 1
            if(j - 1 >= 0 and a[i, j - 1] == 0):
                f = gs(i, (j - 1)) + h2(i, (j - 1))
                if([i + 1, j] not in Close):
                    Open.append([i + 1, j])
                if(f < minf):
                    minf = f
                    i1 = i
                    j1 = j - 1
            b[i, j] = road
            road = road + 1
            i = i1
            j = j1
            Close.append([i + 1, j + 1])
            Opens.extend(Open)
        else:
            end = time.clock()
            b[endx - 1, endy - 1] = road + 1
            print("最短路径寻找成功,路径为:")
            for l in range(len(b)):
                for m in range(b[0].size):
                    #0表示format中第一个元素,>表示右对齐输出,3表示占三个字符
                    print("{0: >4}".format(b[l, m]), end = '')
                print('')
            print("扩展节点数为:", len(Opens))
            print("生成节点数为:", len(Close))
            print('用时为:', end - start)
    
            print('Close表为:', Close)
            print("点的移动轨迹为:")
            for k in range(len(Close)):
                if(k < len(Close) - 1):
                    print(Close[k], "-->", end = '')
                else:
                    print(Close[k])
    
            break
    if(Open == []):
        print("最短路径寻找失败,失败位置为:", Close[-1], "路径为:", Close)
    
    展开全文
  • A*算法求解N数码问题(AI实验一)

    千次阅读 2020-05-21 18:48:01
    利用A*算法求解N数码难题,理解求解流程和搜索顺序 熟练掌握numpy库的相关函数 3.实验原理 A*算法是一种启发式搜索算法,其特点在于对估价函数的定义上。对于一般的启发式搜索,总是选择估价函数f值最小的结点...

    1.实验题目

    A*算法求解N数码问题,要求程序内,输入给定初始状态和目标状态,输出所需步数,过程状态及时间(程序可直接运行,无需再手动输入)。

    2.实验目的及要求

    • 熟悉和掌握启发式搜索的定义、估价函数和算法过程
    • 利用A*算法求解N数码难题,理解求解流程和搜索顺序
    • 熟练掌握numpy库的相关函数

    3.实验原理

    A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的结点作为扩展结点。因此,f是根据需要找到一条最小代价路径的观点来估算结点的,所以,可考虑每个结点n的估价函数值为两个分量:从起始结点到结点n的实际代价以及从结点n到达目标结点的估价代价。
    启发式图搜索的基本特点:如何寻找并设计一个与问题有关的h(n)以及构出f(n)=g(n)+h(n),然后以f(n)的大小来排列待扩展状态的次序,每次选择f(n)的最小值进行扩展。

    • open表:保留所有已生成而未扩展的状态
    • closed表:记录已扩展后的状态
      进入open表的状态是根据估值的大小插入到表中合适的位置,每次从表中优先取出启发估价函数值最小的状态加以扩展。

    4.实验内容

    1. 以8数码问题为例实现A*算法的求解程序
      original state:
    2 8 3
    1 6 4
    7 5

    target state:

    1 2 3
    8 4
    7 6 5

    估价函数f(n)=g(n)+h(n)
    g(n)=d(n) ------结点n在搜索树中的深度
    h(n)可选择h1(n) — 结点n中“不在位”的数码个数 或 h2(n)=p(n)–当前状态到终点的曼哈顿距离
    2. 在求解8数码问题的A* 算法程序中,设置相同的初始状态和目标状态,针对不同的估价函数,求得问题的解,并比较它们对搜索算法性能的影响,包括扩展结点数、生成结点数等
    3. 画出A*算法求解框图

    5.实验分析

    每移动一次空格,就产生一种状态。使用BFS来搜索,但这里使用优先队列而不是普通的队列,这样就可以利用A*算法的核心——评估函数f(n)=g(n)+h(n),对每一种状态用评估函数进行优先队列的排序(只要你给了评估函数的值,优先队列会自己帮我们排序)。
    搜索过程如下:
    在这里插入图片描述

    6.代码实现

    from queue import PriorityQueue
    import datetime
    
    class node:
        def __lt__(self,other):
            return self.cost < other.cost
           
        def __init__(self,n,s,p):
            self.num = n#str的数组
            self.step = s#当前已经走了的步数,int
            self.zeroPos = p#0的位置,int
            self.cost = 0
            self.parent = None#父指针
            self.setCost()
    
        def setCost(self):
            global des
            count = 0
            for i in range(len(des)):
                if self.num[i] != des[i]:
                    count += 1
            self.cost = count + self.step
    
        def setParent(self,father):
            self.parent = father
    
    def swap(li,first,second):
        temp = li[first]
        li[first] = li[second]
        li[second] = temp
    
    def format_p(s,N):
        for i in range(N):
            print('\t'.join(s[i*N:(i+1)*N]))
        print()
        
    def makeChangeId(N):
        #根据N实现changeId
        li = []
        MAX = N**2
        for i in range(MAX):
            temp = []
            row = i//N#行
            col = i%N#列
            if row is not 0:#上,如果行为0,那么肯定不往上移动了
                temp.append(i-N)
            else:
                temp.append(-1)
            if col is not 0:#左
                temp.append(i-1)
            else:
                temp.append(-1)
            if row is not N-1:#下
                temp.append(i+N)
            else:
                temp.append(-1)        
            if col is not N-1:#右
                temp.append(i+1)
            else:
                temp.append(-1)
            li.append(temp)
        return li
    
    def bfs(start,zeroPos):
        #start is str's list
        #zeroPos is int
        global changeId,visit,que,des,N
        #该函数返回最后一个状态即最后一个node
        startNode = node(start,0,zeroPos)
        que.put(startNode)
        while(not que.empty()):
            outNode = que.get()
            strList = outNode.num#这里是list
            strTuple = tuple(strList)#状态表示,这里是tuple
            if strTuple in visit:
                continue
            visit[strTuple] = 1
            pos = outNode.zeroPos#零的位置
            for i in range(4):
                if changeId[pos][i] != -1:
                    swap(strList,pos,changeId[pos][i])               
                    joinTuple = tuple(strList)
                    if strList == des:
                        print('bingo')
                        swap(strList,pos,changeId[pos][i])
                        #找到了先交换回去,因为这里strList是状态对象的成员了,直接返回的话,就不会执行
                        #下面那句swap了,所以这里得加上一句swap
                        return outNode
                    if joinTuple not in visit:
                        new = node(strList.copy(),outNode.step+1,changeId[pos][i])
                        #注意这里必须使用copy,因为不复制传进去的就只是个引用,会导致所有node的成员都是同一个list
                        new.setParent(outNode)
                        que.put(new)
                    swap(strList,pos,changeId[pos][i])
                    
    
    visit = dict()
    que = PriorityQueue()
    print('Please input N:')
    N = eval(input())
    print('Please input the original state:')
    src = input().split()#和之前不同,这里存的都是str的list
    print('Please input the target state:')
    des = input().split()
    for i in range(len(src)):
        if src[i] == '0':
            break
    start_t = datetime.datetime.now()
    changeId = makeChangeId(N)
    last = bfs(src,i)
    
    result = [des]#先装des,用作栈使用
    
    def findroot(last,result):
        result.append(last.num)
        if last.parent == None:
            return
        else:
            findroot(last.parent,result)
            
    findroot(last,result)
    
    end_t = datetime.datetime.now()
    print('The transition from original state to the target state:')
    while(len(result)):
        format_p(result.pop(),N)
    print('Step number:'+str(last.step+1)+'steps')
    print("time = ", (end_t - start_t).total_seconds(), "s")
    print('end')
    
    
    展开全文
  • A*算法求解八数码问题 java实现

    千次阅读 2020-01-27 20:08:00
    A*算法求解八数码问题1.估价函数2.搜索过程3.流程图4.八数码的估计函数设计5.编码实现6.结果 1.估价函数 首先定义估价函数。计算一个节点的估价函数,可以分成两个部分:g(n)已经付出的代价(起始节点到当前节点)...

    1.估价函数

    首先定义估价函数。计算一个节点的估价函数,可以分成两个部分:g(n)已经付出的代价(起始节点到当前节点)和h(n)将要付出的代价(当前节点到目标节点)。节点n的估价函数f(n)定义为f(n)=g(n)+h(n)。在A搜索算法中使用的就是估价函数f(n)=g(n)+h(n)。
    接下来定义最优估价函数f*(n)=g*(n)+h*(n),其中g*(n)为起点到n状态的最短路径代价值,h*(n)是n状态到目的状态的最短路径的代价值。这样f*(n)就是起点出发通过n状态而到达目的状态的最佳路径的总代价值。但在绝大部分实际问题中并不存在f*(n)这样的先验函数,但可以将f(n)作为f*(n)的一个近似估计函数。在A及A搜索算法中,g(n)作为g(n)的近似估价。g(n)与g*(n)可能并不相等,但在搜索过程中有g(n)>=g*(n),当搜索过程中已发现了到达n状态的最佳状态时,它们才相等。同样,可以用h(n)代替h*(n)作为n状态到目的状态的最小代价估计值。虽然在绝大数情况下无法计算h*(n),但是要判别某一h(n)是否大于h*(n)还是可能的。所以如果A搜索算法所使用的估价函数f(x)所能达到的f(n)中的h(n)<=h*(n)时它就为A*搜索算法

    2.搜索过程

    启发式图搜索算法使用两张表记录状态信息:在open表中保留所有已生产而未扩展的状态。在closed表中记录已扩展过的状态。算法中是根据某些启发信息来排列open表的。它既不同于宽度优先使用的队列,也不同于深度优先所使用的堆栈,而是按一个状态的启发估价函数值的大小排列的一个表。进入open表的状态不是简单地排在队尾或队首,而是根据其估值的大小插入到表中合适的位置,每次从表中优先取出启发估价函数最小的状态加以扩展。
    A*算法的搜索过程是基于估价函数的一种加权启发式图搜索算法,搜索过程如下:
    1.把初始结点放入到open表
    2.若open表为空,则搜索失败,退出
    3.移出open表中第一个结点N放入closed表中,也就是估价函数最小的节点,并顺序编号n
    4.若目标结点的状态等于结点N的状态,则搜索成功,结束
    5.若N不可扩展则转步骤2
    6.扩展节点N,生成一组节点N的子节点,对这组子节点做如下操作:
    (1)考察是否有已在open表或closed表中存在的结点。若有则再考察其中有无N的先辈结点,若有则删除之,对于其余节点也删除,但由于它们又被第二次生成,因此需要考虑是否修改已经存在于open表或closed表中的这些结点及其后裔的返回指针和f(x)的值。修改原则是:选f(x)值小的路径走。
    (2)为其余子节点配上指向N的返回指针后放入open表中,并对open表按f(x)值以升序排序,转向步骤2

    3.流程图

    在这里插入图片描述

    4.八数码的估计函数设计

    估计函数是由两部分构成的,节点深度d(n)其实就是当前已经走的步数,不用额外设计函数;启发函数h(n)是比较重要的一个部分,启发函数的设计直接影响了估计函数的效率,有几种定义方法:
    (1)当前节点与目标节点差异的度量 => 当前结点与目标节点相比,位置不符的数字个数
    (2)当前节点与目标节点距离的度量 => 当前结点与目标节点格局位置不符的数字移动到目标节点中对应位置的最短距离之和
    估计函数一:
    八数码的g(n)为已经搜索的步数,八数码的h(n)为当前结点与目标节点差异的数量
    估计函数二:
    当前结点与目标节点格局位置不符的数字移动到目标节点中对应位置的最短距离之和

    5.编码实现

    package Astatr;
    
    import java.util.*;
    
    
    public class Astar {
        private int N=3;
        Scanner input=new Scanner(System.in);
        int Map[][]=new int[N][N];
        int target[][]=new int[N][N];
        List<Node> openList=new ArrayList<>();    //A*算法open列表
        List<Node> closeList=new ArrayList<>();   //A*算法closed列表
    
        List<Node> queue=new ArrayList<>();   //bfs队列
    
        HashMap<Integer,int []> targetmap=new HashMap<>();  //估价函数二所用的映射功能
        List<Node>  nodeList=new ArrayList<>();  //节点列表用于存储所有扩展节点
    
        Node Start;
        Node Target;
        Comparator<Node> comparator=new Comparator<Node>() {    //比较函数,根据f(n)的值可以将openlist或closedlist从小到大排列
            @Override
            public int compare(Node o1, Node o2) {
                if(o1.getF()>o2.getF())
                    return 1;
                else if(o1.getF()==o2.getF())
                    return 0;
                else
                    return -1;
            }
        };
    
        public Astar(){
            if(init()) {
                A_algorithm();    //可以更改为bfs()测试bfs的性能
            }else{
                System.out.println("无解");
            }
        }
        boolean init(){         //初始化函数,用于输入初始八数码和目标八数码
            System.out.println("请输入八数码的初始状态:");
           for(int i=0;i<N;i++){
               for(int j=0;j<N;j++){
                   Map[i][j]=input.nextInt();
               }
           }
           Start=new Node();
           Start.setState(Map);
            System.out.println("请输入八数码的目标状态:");
            for(int i=0;i<N;i++){
                for(int j=0;j<N;j++){
                    target[i][j]=input.nextInt();
                    int index[]={i,j};
                    targetmap.put(target[i][j],index);
                }
            }
           Target=new Node();
            Target.setState(target);
            if(isSolve(Start,Target)){
                return true;
            }else{
                return false;
            }
        }
        public boolean isSolve(Node start,Node target){     //判断是否有解
        		/*
        		对于八数码问题,首先要判断当前的状态能不能到达目的状态,这里需要用到奇排列和偶排列的概念。八数码虽然是个二维数组,但也可以展开看成是一个一维序列。奇排列只能转换成奇排列,偶排列只能转换成偶排列。判断奇偶排列的方法就是:对于每个数,求出排在它之前的比它大的数的个数,然后将这些个数加起来,得到的数是奇数就是奇排列,是偶数就是偶排列,若起始状态和目标状态一个是奇排列一个数偶排列,那么肯定到达不了目标状态。
        		*/
            int startNum[]=start.getSequence();
            int endNum[] =target.getSequence();
            int st = 0;
            int et = 0;
            for (int i = N * N - 2; i >= 0; i--) {
                for (int j = i - 1; j >= 0; j--) {
                    if (startNum[i] > startNum[j])
                        st++;
                    if (endNum[i] > endNum[j])
                        et++;
                }
            }
            if (st % 2 == et % 2)
                return true;
            return false;
        }
    
        int IndexInList(List<Node> list,Node node){    //判断某一状态是否在列表中 
            for (int index = 0; index < list.size(); index++) {
                int i = 0,j=0;
                for (i = 0; i <N; i++) {
                    for(j=0;j<N;j++) {
                        if ((list.get(index).getState()[i][j]) != node.getState()[i][j])
                            break;
                    }
                    if (j < N)
                        break;
                }
                if (i==N&&j==N) {
                    return index;
                }
            }
            return -1;
        }
    
        public boolean isCanMove(int x,int y){    //是否可以移动0
            if(x<0||x>=3||y<0||y>=3){
                return false;
            }
            return true;
        }
        Node getNext(Node now,int direction){   //移动函数,用于获得下一状态
            int dx[]=new int[]{0,0,-1,1};
            int dy[]=new int[]{-1,1,0,0};
            Node next=new Node();
            int temp[][]=new int[N][N];
            for(int i=0;i<N;i++){
                for(int j=0;j<N;j++)
                    temp[i][j]=now.getState()[i][j];
            }
            int zeroIndex[]=now.getZeroIndex();
            int x0=zeroIndex[0];
            int y0=zeroIndex[1];
            int nextZeroIndex=0;
            int nextx,nexty;
            nextx=x0+dx[direction];
            nexty=y0+dy[direction];
            if(isCanMove(nextx,nexty)){
                temp[x0][y0]=now.getState()[nextx][nexty];
                temp[nextx][nexty]=0;        
               List<Node> path=new ArrayList<>();
                path.addAll(now.path);
                next.setState(temp);
                next.setPath(path);
                return next;
            }else{
                return null;
            }
        }
    
        void A_algorithm(){       //A*算法主体
        Start.setUp2();    //设置估价函数,可以更改为其他估价函数
        Start.path.add(Start);
        openList.add(Start);
        nodeList.add(Start);
        while(!openList.isEmpty()){
            openList.sort(comparator);
            Node best=openList.get(0);
            openList.remove(0);
            closeList.add(best);
            if(best.isTarget(Target)){        //判断是否为目标状态
                System.out.println("-------打印路径------");
                for(int i=0;i<best.path.size();i++){
                    System.out.println("第"+i+"次移动");
                    best.path.get(i).print();
                }
                System.out.println("共扩展了"+nodeList.size()+"个节点");
                return;
            }
            for(int i=0;i<4;i++){
                Node next=getNext(best,i);
                if(next!=null){       //是否可以移动到下一个状态
                    if(IndexInList(closeList,next)==-1){     //如果不在cloesd列表中
                        int index=IndexInList(openList,next);
                        if(index>=0){       //如果在open列表中
                            if(next.getG()<openList.get(index).getG()){    //比较和已在open列表中的深度比较
                                openList.remove(index);     //如果next更小,则将open中已存在的更换为next
                                next.setParent(best);
                                next.setUp2(Target,targetmap);   //设置估价函数,可以更改为其他估价函数
                                next.path.add(next);
                                openList.add(next);
                                nodeList.add(next);
                            }
                        }else{    //如果不在open列表中,则加入到open列表中
                            next.setParent(best);
                            next.setUp2(Target,targetmap);   //设置估价函数,可以更改为其他估价函数
                            next.path.add(next);
                            openList.add(next);
                            nodeList.add(next);
                        }
                    }
                }
            }
        }
     }
    
        void bfs(){    //bfs算法
            Start.setUp3();
            Start.path.add(Start);
            queue.add(Start);
            while(!queue.isEmpty()){
                Node best=queue.get(0);
                queue.remove(0);
                if(best.isTarget(Target)){        //判断是否为目标状态
                    System.out.println("-------打印路径------");
                    for(int i=0;i<best.path.size();i++){
                        System.out.println("第"+i+"次移动");
                        best.path.get(i).print();
                    }
                    System.out.println("共扩展了"+nodeList.size()+"个节点");
                    return;
                }
                for(int i=0;i<4;i++){
                    Node next=getNext(best,i);
                    if(next!=null){       //是否可以移动到下一个状态
                        next.setParent(best);
                        next.setUp3();
                        next.path.add(next);
                        queue.add(next);
                        queue.add(next);
                    }
                }
            }
        }
    
         void printPath(Node end){    //打印路径
              while(end!=null){
                  path.add(end);
                  end=end.getParent();
              }
              for(int i=0;i<path.size();i++){
                  System.out.println("---------第"+(i+1)+"次移动--------");
                   path.get(i).print();
                  System.out.println("-------------------------");
              }
             System.out.println("移动次数:"+path.size());
         }
        public static void main(String[] args) {
            Astar astar=new Astar();
        }
    }
    
    
    
    
    
    class Node {       //用于定义状态的类
        private int N=3;
        int state[][]=new int[3][3];
        private int f;    //估计函数
        private int g;    //当前深度
        private int h;     //目标的估计
        private Node parent;  //存储当前结点的上一个状态
        List<Node> path=new ArrayList<>();   //存储路径
        public Node(){
    
        }
    
        public void setUp(Node target) {   //估价函数一
            int num=0;
            for(int i=0;i<3;i++){
                for(int j=0;j<3;j++)
                if(state[i][j]!=target.getState()[i][j]){
                    num++;
                }
            }
            this.h = num;
            if(this.parent==null){
                this.g=0;
            }else{
                this.g=this.parent.getG()+1;
            }
            this.f=this.g+this.h;
        }
        public void setUp2(Node target,Map<Integer,int[]> map){   //估价函数二
            int num = 0;
            for (int row = 0; row < N; row++) {
                for (int cow = 0; cow < N; cow++) {
                    if (cow != 0 && state[row][cow] != target.getState()[row][cow]){
                        num += Math.abs(row - map.get(state[row][cow])[0]) + Math.abs(cow - map.get(state[row][cow])[1]);
                    }
                }
            }
            this.h = num;
            if(this.parent==null){
                this.g=0;
            }else{
                this.g=this.parent.getG()+1;
            }
            this.f=this.g+this.h;
        }
        public void setUp3(){            //bfs的估价函数
            this.h = 0;
            if(this.parent==null){
                this.g=0;
            }else{
                this.g=this.parent.getG()+1;
            }
            this.f=this.g+this.h;
        }
    
        public int[][] getState() {
            return state;
        }
    
        public void setState(int[][] state) {
            this.state = state;
        }
    
        public int getF() {
            return f;
        }
    
        public void setF(int f) {
            this.f = f;
        }
    
        public int getG() {
            return g;
        }
    
        public void setG(int g) {
            this.g = g;
        }
    
        public Node getParent() {
            return parent;
        }
    
        public void setParent(Node parent) {
            this.parent = parent;
        }
    
        public List<Node> getPath() {
            return path;
        }
    
        public void setPath(List<Node> path) {
            this.path = path;
        }
    
        public boolean isTarget(Node target){
            int i = 0,j=0;
            for (i = 0; i <N; i++) {
                for(j=0;j<N;j++) {
                    if (state[i][j]!= target.getState()[i][j])
                        return false;
                }
            }
            return true;
        }
    
        public int[] getZeroIndex(){     //获得0的位置
            int x0 = 0, y0 = 0;
            for (x0 = 0; x0 < N; x0++) {
                boolean flag = false;
                for (y0 = 0; y0 < N; y0++) {
                    if (state[x0][y0] == 0) {
                        flag = true;
                        break;
                    }
                }
                if (flag)
                    break;
            }
            return new int[]{x0, y0};
        }
    
    
       public void print(){    //打印函数
            for(int i=0;i<3;i++){
               for(int j=0;j<3;j++){
                   System.out.print(state[i][j]+" ");
               }
                System.out.println();
            }
       }
    	
    	public int[] getSequence(){     //获取一维序列
        	int num[]=new int[N*N];
        	int count=0;
        	for(int i=0;i<N;i++)
        		for(int j=0;j<N;j++){
        			if(state[i][j]!=0){
    					num[count++]=state[i][j];
    				}
        		}
        	return num;
        }
    }
    
    
    
    
    public class Test(){
    	 public static void main(String[] args) {
            Astar astar=new Astar();
        }
    }
    

    6.结果

    使用0代替空格的位置,并输出打印路径和移动次数
    比较两种不同的估计函数的结果值
    设定初始状态为
    2 8 3
    1 6 4
    7 0 5
    目标状态为
    1 2 3
    8 0 4
    7 6 5

    不同估价函数的比较

    搜索方法 扩展节点数
    A*算法的估价函数一 14
    A*算法的估价函数二 12
    广度优先搜索(BFS) 684

    对比来看不同的估价函数是能够影响搜索效率的,bfs的效率相比于A*算法是低了很多的。

    展开全文
  • 算法流程图

    万次阅读 多人点赞 2013-12-05 15:40:39
    光学习语言的规则还不够,最重要的是学会针对各种类型的问题,拟定出有效的解决方法和步骤即算法。有了正确而有效的算法,可以利用任何一种计算机高级语言编写程序,使计算机进行工作。因此,设计算法是程序设计的...
  • 熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解N数码难题,理解求解流程和搜索顺序。 二、实验原理 A*算法是一种启发式搜索算法,其特点在于对估价函数的定义上。对于一般的启发式搜索,...
  • 算法流程图(及N-S流程图

    千次阅读 2019-08-04 07:12:00
    算法流程图 2.1.1算法 计算机语言只是一种工具。光学习语言的规则还不够,最重要的是学会针对各种类型的问题,拟定出有效的解决方法和步骤即算法。有了正确而有效的算法,可以利用任何一种计算机高级语言编写...
  • 智能算法之免疫算法求解TSP问题

    千次阅读 2020-05-10 21:02:22
    Immunity Algorithm...文章目录1、什么是免疫算法1.1 生物免疫系统1.2 免疫算法的基本原理1.3 免疫算法的基本步骤和流程2、免疫遗传算法3、免疫算法在TSP问题中的应用3.1、免疫算法的结构3.2、求解 TSP 问题流程图3.
  • A*算法求解迷宫

    千次阅读 2014-03-06 16:27:43
    cout下面,您应该根据提示输入各种迷宫的信息,并请求调用A*算法求解"; input(); } //析构函数 ~A_Star() { for(int i=1;i;++i) { for(int j=1;j;++j) { if(_seal[i][j].point!=NULL) { ...
  • A*算法求解15数码问题

    万次阅读 2017-01-11 18:47:37
    A*(A-Star)算法求解最短路最有效的直接搜索方法,也是许多其他问题的常用启发式算法。 其基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的结点来扩展。评价函数形式为:f(n)=g...
  • 寻路问题,输入一个方格表示的地图,要求用A*算法找到并输出从起点(再放各种标示字母S)到终点(在方格中标示字母T)的代价最小的路径,有如下条件及要求: 1.每一步都落在方格中,而不是横竖线的交叉点 2.灰色格子表示...
  • A*算法求解八数码--演示程序(MFC)

    千次阅读 热门讨论 2018-05-11 23:34:13
    首先看看程序的运行效果 一、程序功能: 1、单步或者连续演示用A*解决八数码或者十五数码难题的步骤,可暂停。...A*算法A*算法是一种有序搜索算法,其特点在于对估价函数的定义上。对于一般的...
  • 遗传算法求解TSP问题

    千次阅读 2019-07-07 19:18:52
    分析遗传算法求解不同规模TSP问题的算法性能 城市数量越大或者群体规模越大或者最大迭代数越大,运行等待结果的时间越久 TSP规模越大,用时越长,但是当规模大到一定程度时,遗传算法的求解性能优于...
  • 算法求解步骤:(仅仅为个人理解) 1、分析问题,建立模型 理解题目,属于哪一类题型,与哪类题型类似 2、设计算法,建立初步求解 处理输入(采用哪种数据结构存储) 理清数据处理流程 3、正确性分析 算法...
  • 采用遗传算法求解函数最优值

    千次阅读 2020-12-10 22:00:42
    采用遗传算法求解函数最优值一、实验内容二、实验目的三、实验过程四、实验结果,程序运行结果五、结论六、实验心得七、源程序 一、实验内容 遗传算法(Genetic Algorithms,GA)是一种基于...1.算法基本原理与流程图
  • 弗洛伊德(Froyd)算法用于求解所有顶点到所有顶点的的最短路径。时间复杂度为O(n^3). 正如我们所知道的,Floyd算法用于求最短路径。Floyd算法可以说是Warshall算法的扩展,三个for循环就可以解决问题,所以它的...
  • 遗传算法求解函数最小值问题

    万次阅读 2019-05-17 10:21:51
    遗传算法求解函数最小值问题 继上一次用遗传算法求解TSP问题问题以后,万万没有想到,实验的时候,老师居然改了题目,改成了求解函数的最小值问题(有点坑哈),而且要求结果尽量的稳定,可以确定得到最小值,并且,...
  • 实验6 蚁群算法求解tsp问题

    千次阅读 2019-01-31 22:42:55
    实验5 遗传算法求解tsp问题 实验6 蚁群算法求解tsp问题 实验7 粒子群优化算法求解tsp问题 实验8 分布估计算法求解背包问题 实验9 模拟退火算法求解背包问题 实验10 禁忌搜索算法求解tsp问题 ...
  • 基于粒子群算法求解指定算法的最优参数 引言 粒子群优化算法(Particle Swarm Optimization Algorithm,PSO)是由Eberhart和kennedy提出的一种基于群体协作的随机搜索算法。其原理来源于对鸟群捕食行为的研究,基本...
  • 人工蜂群算法求解TSP问题

    千次阅读 2018-10-19 17:57:04
    人工蜂群算法求解TSP问题 【标签】 ABC TSP Matlab data:2018-10-19 author:怡宝2号 【总起】利用人工蜂群算法(Artificial Bee Colony Algorithm, 简称ABC算法)求解TSP问题,语言:matlab 1. 算法简介 人工蜂群...
  • 运用遗传算法求解函数极值(fortran)写在前面遗传算法的前世今生功能快捷键算法步骤简介遗传算法的主体结构插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右...
  • 粒子群算法求解带约束优化问题 源码实现

    千次阅读 热门讨论 2021-03-29 14:30:32
    粒子群算法求解带约束优化问题 源码实现 python实现。带你体验下粒子群算法是如何求解带等式约束、不等式约束、上下限约束的问题。
  • 遗传算法求解TSP问题 项目源码:传送门 完成日期:2018/12/19 摘要: ​ 利用遗传算法解决TSP问题,TSP问题的规模大小为131个城市。遗传算法的基本思想是使用模拟生物和人类进化的方法求解复杂的优化问题。利用遗传...
  • PSO算法求解整数规划问题举例

    千次阅读 2020-02-23 21:26:06
    前言:最近在使用群智能算法求解线性规划问题的过程中,由于初次设计该方面的知识,在网络上检索了许多相关的智能算法的案例进行学习。首先选择了粒子群算法求解线性规划问题,但是网络上已有的一些资料对于非专业...
  • 蚁群算法求解路径优化问题

    千次阅读 2019-06-12 17:10:26
    蚁群算法求解CVRP问题matlab代码,可直接运行 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的...
  • 采用匈牙利算法求解分配问题

    千次阅读 2020-05-21 17:25:48
    采用匈牙利算法求解分配问题什么是分配问题?匈牙利算法 什么是分配问题?   分配问题也称指派问题,是一种特殊的整数规划问题,分配问题的要求一般是这样的:n个人分配n项任务,一个人只能分配一项任务,一项任务...
  • 匈牙利算法求解指派问题基于以下原理:在一个成本矩阵中,对某一行或者某一列加上或减去一个数,最优的分配方案不变。基于此原理,我们可以对成本矩阵进行变换,直到使用试指派能够找到最优解(对一个n*n的成本矩阵...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 28,832
精华内容 11,532
关键字:

a算法求解流程图