迷宮問題求解之“A*搜索”(二)

来源:http://www.cnblogs.com/mingjiatang/archive/2016/10/16/5967681.html
-Advertisement-
Play Games

摘要:在 "迷宮問題求解之“窮舉+回溯”(一)" 這篇文章中採用“窮舉+回溯”的思想,雖然能從迷宮的入口到出口找出一條簡單路徑,但是找出來的不是最優路徑。因此本文采用A 搜索演算法,求解迷宮問題的最優路徑。 1 A 搜索演算法簡介 A 搜索演算法是一種啟髮式搜索演算法。所謂啟髮式搜索演算法,就是在盲目搜索演算法 ...


摘要:在迷宮問題求解之“窮舉+回溯”(一)這篇文章中採用“窮舉+回溯”的思想,雖然能從迷宮的入口到出口找出一條簡單路徑,但是找出來的不是最優路徑。因此本文采用A*搜索演算法,求解迷宮問題的最優路徑。

1 A*搜索演算法簡介

A*搜索演算法是一種啟髮式搜索演算法。所謂啟髮式搜索演算法,就是在盲目搜索演算法中加入一個啟發函數,在當前節點搜索完畢後,通過這個啟發函數來進行計算,選擇代價最少的節點作為下一步搜索的節點。通過這樣的方式就能夠找到最優解。

DFS,BFS這兩種搜索方式都屬於盲目的搜索方式,它不會在選擇下一個節點的時候進行代價計算,而是按照一個固定的方式選擇,這樣在運氣不好的情況,會對所有節點進行遍歷。

A*搜索演算法的核心就在於如何設計一個好的啟發函數,啟發函數的表達形式一般如下:
$$
f(n)=g(n)+h(n)
$$
其中$f(n)$是每個節點可能的估值或者代價值,它由兩部分組成。

  • $g(n)$:它表示從起點到搜索點的代價。
  • $h(n)$:它表示從搜索點到目標點的代價,$h(n)$設計的好壞,直接影響到該A*演算法的效率。

一種具有$f(n)=g(n)+h(n)$策略的啟髮式演算法能成為A*演算法的充分條件是:

  1. 搜索樹上存在著從起始點到終了點的最優路徑。

  2. 問題域是有限的。

  3. 所有結點的子結點的搜索代價值都大於零。

  4. $h(n)=<h(n)$ ($h(n)$為實際問題的代價值)。

很顯然,1,2,3點都很容易滿足的。關鍵是設計一個好的$h(n)$函數。

2 A*搜索演算法描述

A*演算法的流程如下:

A*搜索演算法需要兩個額外的存儲空間,OPEN表和CLOSE表。

  1. 初始化OPEN和CLOSE表,將開始節點(開始節點的H和G的值都視為0)放入OPEN表中。

  2. 重覆下麵步驟:(迴圈)

    2.1 在開始列表中查找具有最小F值的節點,並把查找到的節點作為當前節點;

    2.2 把當前節點從OPEN列表中刪除,並加入到CLOSE列表中;

    2.3 對當前節點相鄰的每一個節點依次執行以下步驟:(遍歷) i 如果相鄰節點不可通行或者該節點已經在CLOSE列表中,則什麼操作也不執行; ii 如果該相鄰節點不在OPEN列表中,則將該節點添加到OPEN列表中,並將該相鄰節點的父節點設置當前節點,同時計算保存相鄰節點的F值;iii 如果該相鄰節點在OPEN表中, 則判斷若經由當前節點到達該相鄰節點的F值是否小於原來保存的F值,若小於,則將該相鄰節點的父節點設為當前節點,並重新設置該相鄰節點的F值.

    2.4 若當終點節點被加入到開發列表作為待檢驗節點時,表示路徑已找到,此時應終止迴圈;若當開放列表為空,表示沒有中開始節點到終點節點的路徑,此時迴圈結束;

  3. 迴圈終止後,從終點節點開始沿著父節點往前遍歷,從後到前輸出搜索的路徑。

其中,步驟2.3 ii中的紅色部分尤為重要,設置相鄰節點的父節點為當前節點是為了記錄從起始節點到終止節點路徑,方便搜索到了終點節點後進行路徑輸出。

在採用A* 演算法對迷宮路徑求解中,$g(n)$和$h(n)$函數都採用曼哈頓距離,曼哈頓距離代表兩個點在標準坐標繫上的絕對軸距總和。
$$
d(i,j)=|x1-x2|+|y1-y2|
$$
即每次獲取的當前通道塊,都會對其到入口通道快和出口通道塊的曼哈頓距離進行計算。求算當前通道塊的F值進行保存。該函數滿足啟髮式演算法成為A*演算法的充分條件的1,2,3,4。

還需要註意的一點是:使用A*求解迷宮路徑演算法中,需要把2.3步驟下麵的遍歷操作進行更改,如下:

i 如果相鄰節點不可通行或者該節點已經在CLOSE或者OPEN列表中,則什麼操作也不執行。

ii 如果該相鄰節點不在OPEN和CLOSE列表中,則將該節點添加到OPEN列表中,並將該相鄰節點的父節點設置當前節點,同時計算保存相鄰節點的F值。

在迷宮求解中使用A*搜索演算法不需要去更新OPEN表中的已存在節點的F值,因為每個節點的位置都是確定的,所以曼哈頓距離就固定的。如果是帶權值的路網求解最短路徑,那麼就需要去更新OPEN表中節點的F值,如Dijkstra演算法。

3 程式實現

下麵使用使用c#語言,實現A*演算法求解迷宮最優的路徑。關鍵代碼如下:

3.1 節點實體類

class PathNode
{
    public PathNode(int row, int col)
    {
        this.Row = row;
        this.Col = col;
    }
    public int Row;
    public int Col;
    public int F=int.MaxValue;
    public PathNode Father=null;//該節點的前繼節點
}

3.2 A*搜索類

class AStartSolution
{
    int [,]map;
    int startX;
    int startY;
    int endX;
    int endY;
    public AStartSolution (int [,]map,int startX,int startY,int endX,int endY)
    {
        this.map=map;
        this.startX =startX;
        this.startY=startY;
        this.endX=endX;
        this.endY=endY ;
    }
    /*路徑尋找*/
    public  PathNode FindPath()
    {
        PathNode endNode = null;//終點
        List<PathNode> openList = new List<PathNode>();
        List<PathNode> closeList = new List<PathNode>();
        //把起點放入open列表中
        PathNode startNode = new PathNode(startX, startY);
        openList.Add(startNode);
        int tryCount = 0;
        while (openList.Count != 0)
        {
            tryCount++;
            PathNode curMinFNode = GetMinFPathNode(openList);
            openList.Remove (curMinFNode );//從open列表中移除
            closeList .Add (curMinFNode );//加入到close列表中
            if (curMinFNode.Row == endX && curMinFNode.Col == endY)//當前節點是目標節點
            {
                endNode = curMinFNode;
                break;
            }
            else//搜索4個方向併進行處理
            {
                    
                //東
                HandleChildNode(curMinFNode, curMinFNode.Row, curMinFNode.Col + 1, openList, closeList);
                //南
                HandleChildNode(curMinFNode, curMinFNode.Row+1, curMinFNode.Col, openList, closeList);
                //西
                HandleChildNode(curMinFNode, curMinFNode.Row, curMinFNode.Col-1, openList, closeList);
                //北
                HandleChildNode(curMinFNode, curMinFNode.Row-1, curMinFNode.Col, openList, closeList);
            }
        }
        return endNode;
    }

    /*處理每個方向的子節點*/
    private  void HandleChildNode(PathNode curMinFNode,int row, int col, List<PathNode> openList, List<PathNode> closeList)
    {
        PathNode child = null;
        if (Pass(row, col))//能通過
        {
            child = new PathNode(row, col);
            if (!IsExistList(child, closeList) && !IsExistList(child, openList))//
            {
                child.F = GetF(row,col);
                child.Father = curMinFNode;
                openList.Add(child);
            }
        }
    }
    private PathNode GetMinFPathNode(List<PathNode> openList)
    {
        int minF= openList.Min(node =>node.F);
        foreach (PathNode node in openList)
        {
            if (node.F == minF)
            {
                return node;
            }    
        }
        return null;
    }
    /*該通道塊是否可通行*/
    private  bool Pass(int row,int col)
    {
        int rowCount = map.GetLength(0);
        int colCount = map.GetLength(1);
        //邊界判斷
        if (row >= 0 && row < rowCount && col >= 0 && col < colCount)
        {
            //障礙判斷
            if (map[row, col] == 2)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        else
        {
            return false;
        }
    }

    /*當前通道塊是否在OPEN或者CLOSE表中*/
    private  bool IsExistList(PathNode childNode, List<PathNode> list)
    {
        foreach (PathNode node in list)
        {
            if (node.Row == childNode.Row && node.Col == childNode.Col)
            {
                return true;
            }
        }
        return false;
    }

    /*計算當前節點的F值*/
    private  int GetF(int curRow, int curCol)
    {
        int g = Math.Abs(startX - curRow) + Math.Abs(startY - curCol);
        int h = Math.Abs(endX - curRow) + Math.Abs(endY - curCol);
        return g + h;
    }
}

3.3 與“窮舉+回溯”演算法的效果比較

採用同樣的迷宮地圖比較兩種演算法查找的路徑,上結果圖代表“窮舉+回溯,下結果圖代表本程式

2016_10_bb9640ef-e5ab-483b-9e6c-5951ab52e2ac

4 總結

通過上面的比較可以發現,A*搜索演算法尋找的路徑比“窮舉+回溯”演算法的要短很多,和我設計迷宮地圖時所想的路徑是一致,是一條最優的路徑。

5 資源和參考資料

參考資料:

莫水千流的博文A星尋路演算法介紹
西芒xiaoP的博文A*搜索演算法
yueming的博文A*路徑尋找演算法入門 

源碼地址:

http://download.csdn.net/download/mingge38/9655373


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 借鑒鏈接:http://www.cr173.com/html/67361_1.html Win10提示沒有許可權使用網路資源解決方法 1、打開控制面板; 2、在所有控制面板項中找到憑據管理器; 3、添加windows憑據; 4、在地址欄填對方電腦名,用戶名guest,密碼空; 5、在對方電腦的用戶設 ...
  • 在FFT處理線面呢,很多人就說要加窗,加窗的好處了就是防止能量泄露和高頻濾波啊,不過精度呢就會相應的降低。(聽說是這樣的。本人小白) 窗的種類也很多啦,然後聽說啥都不懂的就可以了選擇漢寧窗。。。 在MATLAB裡面呢直接調用hann(); 然後呢在stm32裡面呢就直接一個for。。。。。。感覺用了 ...
  • 感覺直接貼代碼會好點。。。。。。 有些註釋直接從Keil5裡面粘出來到這裡就不支持了。。。。。。。好尷尬。。。。下次碼代碼註釋還是全英算了、、、 哈哈。。有什麼問題可以一起來探討、、、不知道為啥分類不到嵌入式那裡只好點Linux那裡了。。 ...
  • ST公司為了方便客戶使用FFT,自己做了一個庫,不過這個庫是有限制的。點數必須是4的次方,分別是64、256和1024個點。速度完全滿足客戶的要求。 1、第一步必須添加使用FFT的庫文件到inc和src中。附上百度雲網盤鏈接(http://pan.baidu.com/s/1gfHkS0b) 2、導入 ...
  • 使用一個簡單的for迴圈和if判斷語句實現某個網段內所有ping所有客戶機的shell程式: 在這裡i是一個迴圈變數,一共迴圈254次,${i}相當於192.168.10.0這個網段中從1~254的主機號。 for迴圈開始然後進行if判斷: 判斷 ping 192.168.10.xxx這個網段中的所 ...
  • readonly 相當於C中的const,readonly將變數設為只讀模式,任何針對他們的賦值都是錯誤的 export 修改或列印環境變數,可以將變數放在環境里,放到環境里的變數可供所有的進程通過環境共用 unset 刪除變數 刪除變數var_name 刪除其他變數 刪除函數 shift 用來截去 ...
  • 一、簡單介紹 1、LinQ to Sql類(NET Language Integrated Query (LINQ) ) LINQ定義了大約40個查詢操作符,如select、from、in、where以及order by(C#中)。使用這些操作符可以編寫查詢語句。不過,這些查詢還可以基於很多類型的數 ...
  • 最近在做.net項目,因為本人以前做java較多,所以對.net不熟悉,在項目完成後部署到IIS伺服器上出現諸多問題,以上其中之一,若有時間,在更新其他問題的解決辦法! 異常詳細信息: System.Data.SqlClient.SqlException: 用戶 'NT AUTHORITY\IUSR ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...