人工智能初级路径搜索算法——如何寻找确定环境中最佳路径的策略(一)

Optimal Strategies in Deterministic Environments

  • The Simple Case
  • Path Finding
    • 广度优先搜索(Breadth-first Search)
  • 路径搜索策略代码练习
    • 迷宫世界的生成
    • 通用代码的思考
    • BFS
      • 小菜鸟的代码调bug日志

首先,我们先回顾人工智能的概论,有一个初步的认识。
人工智能初级路径搜索算法——如何寻找确定环境中最佳路径的策略(一)_第1张图片

The Simple Case

Modelling successive states,我们常用的方法是directed graph。

Exercise
A farmer, a wolf, a goat, and a cabbage are on one side of a river that they must cross.
There is a boat that can be operated only by the farmer, and with only two places. When
the farmer is not there, the wolf will eat the goat, and the goat will eat the cabbage.

  1. Draw a directed graph modelling this problem. You should not expand the nodes in which the “game” is won or lost;
  2. Give a strategy for the farmer allowing everyone to safely cross the river.
    人工智能初级路径搜索算法——如何寻找确定环境中最佳路径的策略(一)_第2张图片
    在这里面,我们扩展两个术语。timed automata和hybrid automata。
    如果我们想要使系统实现某一个目标,我们需要一定的策略和规划。
    首先,感性理解一下我们的路径搜索算法。我们来看个Java小动画。开源代码在创作者的Github里面。我们将会学习其中的部分算法。
    人工智能初级路径搜索算法——如何寻找确定环境中最佳路径的策略(一)_第3张图片

Path Finding

广度优先搜索(Breadth-first Search)

BFS可用来解决计算机游戏(例如即时策略游戏)中找寻路径的问题。在这个应用中,使用平面网格来代替图形,而一个格子即是图中的一个节点。所有节点都与它的邻居(上、下、左、右、左上、右上、左下、右下)相接。
值得一提的是,当这样使用BFS算法时,首先要先检验上、下、左、右的邻居节点,再检验左上、右上、左下、右下的邻居节点。这是因为BFS趋向于先查找斜向邻居节点,而不是四方的邻居节点,因此找到的路径将不正确。BFS应该先查找四方邻居节点,接着才查找斜向邻居节点。 ——维基百科

路径搜索策略代码练习

迷宫世界的生成

我们搜索路径的环境应用以下代码,代码作者Didier Lime的主页可以浏览

// Example of world building, display, and successor computation for the artificial 
// intelligence path-finding lab
//
// Author: Didier Lime
// Date: 2018-10-03

#include <iostream>
#include <list>
#include <cstdlib>
#include <ctime>

using namespace std;

class World
{
    private:
        // Number of columns
        unsigned int L;
        
        // Number of lines
        unsigned int H;

        // Unidimensional array for tiles
        int* w;
    
    public:
        // Constructor
        World(unsigned int L, unsigned int H, double P)
        {
            this->L = L;
            this->H = H;
            this->w = new int[L*H];

            // Add walls to the first and last columns
            for (unsigned int i = 0; i < H; i++)
            {
                this->w[i * L] = 1;
                this->w[i * L + L - 1] = 1;
            }

            // Add walls to the first and last lines
            for (unsigned int j = 0; j < L; j++)
            {
                this->w[j] = 1;
                this->w[(H - 1) * L + j] = 1;
            }

            for (unsigned int i = 0; i < H; i++)
            {
                for (unsigned int j = 0; j < L; j++)
                {
                    // add a wall in this tile with probability P and provided that it is neither
                    // the starting tile nor the goal tile 
                    if ((double) rand() / RAND_MAX < P && !(i == 1 && j == 1) && !(i == H - 2 && j == L - 2))
                    {
                        this->w[i * L + j] = 1;
                    }
                }
            }
        }

        // Display the world
        void display()
        {
            for (unsigned int i = 0; i < H; i++)
            {
                for (unsigned int j = 0; j < L; j++)
                {
                    switch (this->w[i * L + j])
                    {
                        case 0:
                            cout << ".";
                            break;

                        case 1:
                            cout << "W";
                            break;
                    }
                }
                cout << endl;
            }
        }

        // compute the successors of tile number i in world w
        // we return the number n of valid successors
        // the actual list is in array r where only the first n
        // elements are significant
        unsigned int successors(unsigned int i, unsigned int r[4])
        {
            unsigned int n = 0;

            if (i >= 0 && i < this->L * this->H && this->w[i] != 1)
            {
                // if i is a correct tile number (inside the array and not on a wall)
                // look in the four adjacent tiles and keep only those with no wall
                const unsigned int moves[] = { i - 1, i + 1, i - L, i + L};
                
                for (unsigned int k = 0; k < 4; k++)
                {
                    if (this->w[moves[k]] != 1)
                    {
                        r[n] = moves[k];
                        n++;
                    }
                }
            }

            return n;
        }

        // Depth-first search
        // starting from tile number s0, find a path to tile number t
        // return true if such a path exists, false otherwise
        // if it exists the path is given in variable path (hence the reference &)
        bool dfs(unsigned int s0, unsigned int t, list<unsigned int>& path)
        {
            bool r = false;

            // ... Complete here ...

            return r;
        } 
};

int main()
{
    // Initialise the random number generator
    srand(time(0));

    // Create a world
    World w(20, 10, 0.2);

    // Display it
    w.display();

    // Print the tile numbers of the successors of the starting tile (1, 1)
    unsigned int succs[4];
    unsigned int n = w.successors(21, succs);

    for (unsigned int k = 0; k < n; k++)
    {
        cout << succs[k] << " ";
    }
    cout << endl;

    return 0;
}

初始环境的设立如图所示。
人工智能初级路径搜索算法——如何寻找确定环境中最佳路径的策略(一)_第4张图片

通用代码的思考

无论用到什么算法,我们都需要设置开始位置和目标位置。也需要有记录当前位置和离开出发点的距离。

struct position
{
    int x;
    int y;
};
//typedef struct point pt;可以改写名字
typedef pair<point,int> p;//用pair存储位置和距离  
//这种情况是我们在建立地图时候就随机生成了起点和终点。
position start_point,end_point;
for(int i=1;i<=n;i++){  //遍历行
        for(int j=1;j<=m;j++){  //遍历列
            cin>>mp[i][j];      //输入
            if(mp[i][j]=='s'){  //如果是起点
                start_point.x=i;      //记下X
                start_point.y=j;      //记下Y
            }
            if(mp[i][j]=='g'){//如果是终点
                end_point.x=i;      //记下X
                end_point.y=j;      //记下Y
            }
        }
    }
//如果我们已经知道了两点的具体位置
//A robot tries to move from coordinates (1, 1) (top-left of the screen)
//to coordinates (L−2, H −2) (bottom-right of the screen) 
position start_point={{1,1}};
position end_point={{L-2,H-2}};

BFS

广度优先搜索Breadth First Search

小菜鸟的代码调bug日志

为了更好的理解BFS搜索算法,我们来看一看相关代码。

std::queue<node *> visited, unvisited;

node nodes[9];

node *current;


unvisited.push(&nodes[0]); // 先把root放入unvisited queue


while (!unvisited.empty()) { // 只有unvisited不空

    current = (unvisited.front()); // 目前應該檢驗的

    if (current->left != NULL)

        unvisited.push(current->left); // 把左邊放入queue中

    if (current->right != NULL) // 右邊壓入。因為QUEUE是一個先進先出的結構构,所以即使後面再壓其他东西,依然會先訪問這個。

        unvisited.push(current->right);

    visited.push(current);

    cout << current->self << endl;

    unvisited.pop();

}

直接运行是会出错的。天下就是没有免费的午餐,或早或晚,一定是要付出代价的。

bfs.cpp:1:6: error: ‘queue’ in namespace ‘std’ does not name a template type
 std::queue<node *> visited, unvisited;
      ^~~~~
bfs.cpp:3:1: error: ‘node’ does not name a type
 node nodes[9];
 ^~~~
bfs.cpp:5:1: error: ‘node’ does not name a type
 node *current;
 ^~~~
bfs.cpp:8:1: error: ‘unvisited’ does not name a type; did you mean ‘unsigned’?
 unvisited.push(&nodes[0]); // 先把root放入unvisited queue
 ^~~~~~~~~
 unsigned
bfs.cpp:11:1: error: expected unqualified-id before ‘while’
 while (!unvisited.empty()) { // 只有unvisited不空
 ^~~~~

首先是没有包含全头文件,要添加相关#include<>文件。
其次是没有node这个数据类型,自己要添加struct或者class。

bfs.cpp: In function ‘int main()’:
bfs.cpp:24:18: error: ‘struct main()::node’ has no member named ‘left’
     if (current->left != NULL)                  ^~~~

然后,我们建立的node里面要有成员left,right,self。不然依然在报错。
累了,休息一会儿,阅读别人的代码看看。

你可能感兴趣的:(C++学习笔记)