Android啟發式尋路
實現的效果圖
Screenshot_20181213-143828.jpeg
思路分析
根據啟發演算法理論 f(n) = g(n)+h(n); 其中,g(n)表示實際代價(即已經走過的路程),h(n)代表預估代價,由於使用的網格構造,所以使用曼哈頓距離公式,表示h(n)。
程式碼實現
資料結構(採用單向連結串列)
public class Node implements Comparable<Node> { Coord coord; Node parent;int g; // G:是個準確的值,是起點到當前結點的代價int h; // H:是個估值,當前結點到目的結點的估計代價public Node(int x, int y) { this.coord = new Coord(x,y); }public Node(int x, int y, Node parent, int h, int g) { this.coord = new Coord(x,y); this.parent = parent; this.h = h; this.g = g; }public Node getParent() { return parent; }public void setParent(Node parent) { this.parent = parent; }public int getH() { return h; }public void setH(int h) { this.h = h; }public int getG() { return g; }public void setG(int g) { this.g = g; }@Overridepublic int compareTo(@NonNull Node o) { return g+h - (o.g+o.h); }@Overridepublic boolean equals(Object obj) { Node node = (Node) obj; return coord.x == node.coord.x && coord.y == node.coord.y; }
}
說明:
由於尋路需要透過一個點找附近最近的點,最終形成一條多段的線段,在資料結構中與單向連結串列吻合,故選擇單向連結串列儲存。compareTo方法是按f(n)大小排序。
核心程式碼思路
1.將起點加入開放列表
2.從開放列表中移除掉其中f(n)最小的一個Node,並將該節點加入到關閉列表
3.從該節點向周圍擴充套件,如果符合條件,則加入到開放列表
4.重複23步驟直至終點與2步驟的節點重合
程式碼:
public class Astart { private int[][] map; private int width; private int height; private Node start; private Node end; private PriorityQueue<Node> openList = new PriorityQueue<>(); private List<Node> closeList = new ArrayList<>(); public Astart(@NonNull int[][] map, @NonNull Node start, @NonNull Node end) { this.map = map; this.start = start; this.end = end; height = map.length; width = map[0].length; }public Stack<Node> start() { //將起點放入開放列表 openList.add(start); while (!openList.isEmpty()) { //將曼哈頓距離最近的點取出 Node node = openList.poll(); //將節點放入關閉列表中 System.out.println(node.coord); closeList.add(node); //如果取出來的最近的點為終點,結束迴圈 if (end.equals(node)) { end = node; break; } //將擴充套件的節點加入開放列表(朝八個方向) addNeighborNode(node); } //把關閉列表選中 Node node = end; //將資料依次放入棧實現連結串列倒序 Stack<Node> pathStack = new Stack<>(); while (node != null) { pathStack.push(node); node = node.parent; } return pathStack; }/** * 向八個方向擴充套件新增節點 * * @param node */private void addNeighborNode(Node node) { Node left = new Node(node.coord.x - 1, node.coord.y); left.setParent(node); left.setG(generateG(left)); left.setH(generateH(left)); Node right = new Node(node.coord.x + 1, node.coord.y); right.setParent(node); right.setG(generateG(right)); right.setH(generateH(right)); Node top = new Node(node.coord.x, node.coord.y - 1); top.setParent(node); top.setG(generateG(top)); top.setH(generateH(top)); Node bottom = new Node(node.coord.x, node.coord.y + 1); bottom.setParent(node); bottom.setG(generateG(bottom)); bottom.setH(generateH(bottom)); Node leftTop = new Node(node.coord.x - 1, node.coord.y - 1); leftTop.setParent(node); leftTop.setG(generateG(leftTop)); leftTop.setH(generateH(leftTop)); Node leftBottom = new Node(node.coord.x - 1, node.coord.y +1); leftBottom.setParent(node); leftBottom.setG(generateG(leftBottom)); leftBottom.setH(generateH(leftBottom)); Node rightTop = new Node(node.coord.x + 1, node.coord.y - 1); rightTop.setParent(node); rightTop.setG(generateG(rightTop)); rightTop.setH(generateH(rightTop)); Node rightBottom = new Node(node.coord.x + 1, node.coord.y + 1); rightBottom.setParent(node); rightBottom.setG(generateG(rightBottom)); rightBottom.setH(generateH(rightBottom)); addNeighborSingleNode(left); addNeighborSingleNode(right); addNeighborSingleNode(top); addNeighborSingleNode(bottom); // addNeighborSingleNode(leftTop); // addNeighborSingleNode(leftBottom); // addNeighborSingleNode(rightTop); // addNeighborSingleNode(rightBottom);}/** * 單獨新增一個相鄰節點 * * @param node */private void addNeighborSingleNode(Node node) { if (canAdd(node)) { openList.add(node); } }private int generateG(Node node) { if (node.parent != null) { Node parent = node.parent; int c = (Math.abs(start.coord.x - node.coord.x) + Math.abs(start.coord.y - node.coord.y))*100; return parent.h + c; } return 0; }private int generateH(Node node) { return Math.abs(end.coord.x - node.coord.x) + Math.abs(end.coord.y - node.coord.y)*100; }/** * 能否新增進開放列表 * * @param node * @return */private boolean canAdd(Node node) { if (node == null || node.coord == null) { return false; } int x = node.coord.x; int y = node.coord.y; //如果超過邊界 false if (x < 0 || x > width - 1) { return false; } if (y < 0 || y > height - 1) { return false; } //如果節點在地圖上位牆壁 false int mapPoint = map[y][x]; if ( mapPoint == MapUtils.WALL) { return false; } //如果節點在開放列表或者關閉列表中 false if (openList.contains(node)) { return false; } if (closeList.contains(node)) { return false; } return true; }
介面程式碼
public class MainActivity extends AppCompatActivity {private Node start;private Node end;private ShowMapView showMapView;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); showMapView = findViewById(R.id.show); showMapView.init(MapUtils.map, new ShowMapView.OnTouchListener() { @Override public void onTouchPoint(int type, int row, int col) { if (type == 1) { start = new Node(col, row); } else { end = new Node(col, row); } } }); }private void findWayAndDraw() { if (start != null && end != null) { Stack<Node> pathList = new Astart(MapUtils.map, start, end).start(); Path path = new Path(); path.moveTo(start.coord.x * 80 + 40, start.coord.y * 80 + 40); while (!pathList.empty()) { Node node = pathList.pop(); path.lineTo(node.coord.x * 80 + 40, node.coord.y * 80 + 40); } showMapView.setPath(path); } }public void cal(View view) { findWayAndDraw(); }public void reset(View view) { showMapView.reset(); } }
佈局檔案
<RelativeLayout xmlns:android=""xmlns:app=""xmlns:tools=""android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.example.n011210.findway.ShowMapView android:id="@+id/show" android:layout_width="match_parent" android:layout_height="match_parent" /><Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:onClick="cal" android:text="計算" /><Button android:id="@+id/reset" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_toRightOf="@id/btn" android:onClick="reset" android:text="重新整理" /> </RelativeLayout>
說明
ShowMapView為地圖控制元件
public class ShowMapView extends View { private int touchFlag; private int[][] map; private Path path; private OnTouchListener listener; public ShowMapView(Context context) { super(context); }public ShowMapView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); }public ShowMapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }public void init(int[][] map, OnTouchListener listener) { this.map = map; this.listener = listener; invalidate(); }public void setPath(Path path) { this.path = path; invalidate(); }public void reset() { touchFlag = 0; for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[i].length; j++) { if (map[i][j] == 2) { map[i][j] = 0; } } } path.reset(); invalidate(); } @Overridepublic boolean onTouchEvent(MotionEvent event) { if (touchFlag >= 2) { return super.onTouchEvent(event); } float x = event.getX(); float y = event.getY(); //每格地圖大小為80*80,注意:陣列和螢幕座標X和Y相反 int row = (int) y / 80; int col = (int) x / 80; if (map[row][col] == 0) { touchFlag++; if (listener != null) { listener.onTouchPoint(touchFlag, row, col); } map[row][col] = 2; } this.invalidate(); return super.onTouchEvent(event); } @Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); if (map == null) { return; } Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.BLUE); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[i].length; j++) { if (map[i][j] == 0) { Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.route); canvas.drawBitmap(bm, j * 80, i * 80, paint); } else if (map[i][j] == 1) { Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.wall); canvas.drawBitmap(bm, j * 80, i * 80, paint); } else if (map[i][j] == 2) { Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.path); canvas.drawBitmap(bm, j * 80, i * 80, paint); } } } if (path != null) { canvas.drawPath(path, paint); } }public interface OnTouchListener { void onTouchPoint(int type, int row, int col); } }
作者:夜亦明
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3244/viewspace-2821636/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- A*啟發式搜尋
- 最佳路徑搜尋(二):啟發式搜尋(代價一致搜尋(Dijkstra search),貪心搜尋,A*搜尋)
- 人工智慧 (09) 啟發式搜尋人工智慧
- js版九宮格拼圖與啟發式搜尋(A*演算法)JS演算法
- A*尋路
- 啟發式合併
- 啟發式搜尋的方式(深度優先,廣度優先)和 搜尋方法(Dijkstra‘s演算法,代價一致搜尋,貪心搜尋 ,A星搜尋)演算法
- 如何啟用Win10的沉浸式搜尋欄Win10
- Android開發_在Android Studio中搜尋專案中出現過的字串Android字串
- 遊戲AI尋路——八叉樹+A*尋路遊戲AI
- Win10 1903系統如何啟用沉浸式搜尋欄Win10
- 樹上啟發式合併
- 尋寶路線
- zoj-4053(2018ICPC青島網路賽K題)啟發式分裂
- 卷積神經網路-啟用函式卷積神經網路函式
- Android開發:修改eclipse裡的Android虛擬機器路徑AndroidEclipse虛擬機
- Flutter 開發 Android & IOS 啟動頁 splash pageFlutterAndroidiOS
- Android本地搜尋最佳化Android
- 千尋位置啟動全球最大規模北斗高精度定位路測
- TSP問題(換位表達,啟發式交叉,啟發式變異)C++實現C++
- A*與“一般的啟發式”
- unity 自動尋路Unity
- 不走尋常路
- Android 開發者學習路線(2020 版)Android
- 【Android 開發 VR 實戰】三. 開發一個尋寶類 VR 遊戲 TreasureHuntAndroidVR遊戲
- 直播軟體開發,自定義搜尋欄的圖示樣式和搜尋框
- 神經網路的啟用函式總結神經網路函式
- 神經網路中使用的啟用函式神經網路函式
- Android啟動模式Android模式
- 樹上啟發式合併總結
- SOAR的啟發式規則建議
- elasticsearch(五)---分散式搜尋Elasticsearch分散式
- 神經網路啟用函式=生物轉換器?神經網路函式
- Linux搜尋啟動工具FindexLinuxIndex
- Android 尋找極限編碼的「快感」Android
- Android開發之無侵入式修改TabLayout tabIndicator寬度AndroidTabLayoutIndicator
- 人工智慧---神經網路啟用函式恆等函式、sigmoid函式、softmax函式詳解人工智慧神經網路函式Sigmoid
- Android開發中網路安全性配置問題Android