Algorithm 编程理论:解迷宫

Algorithm 编程理论:解迷宫,algorithm,maze,Algorithm,Maze,解决迷宫的可能方法有哪些? 我有两个想法,但我认为它们不太优雅 基本情况:我们有一个矩阵,这个矩阵中的元素以一种表示迷宫的方式排列,一个是进,一个是出 我的第一个想法是送一个机器人穿过迷宫,跟随一边,直到它走出迷宫。我认为这是一个非常缓慢的解决方案 第二个路径通过每个标有1的连续项目,检查它可以到达的位置(上、右、下、左)选择一条路径并继续其路径。这比第一个还要慢 当然,如果我让两个机器人在每个连接点都多线程,速度会快一点,但这也不是最好的方法 需要有更好的解决方案让机器人通过迷宫 编辑 第一:

解决迷宫的可能方法有哪些?
我有两个想法,但我认为它们不太优雅

基本情况:我们有一个矩阵,这个矩阵中的元素以一种表示迷宫的方式排列,一个是进,一个是出

我的第一个想法是送一个机器人穿过迷宫,跟随一边,直到它走出迷宫。我认为这是一个非常缓慢的解决方案

第二个路径通过每个标有1的连续项目,检查它可以到达的位置(上、右、下、左)选择一条路径并继续其路径。这比第一个还要慢

当然,如果我让两个机器人在每个连接点都多线程,速度会快一点,但这也不是最好的方法

需要有更好的解决方案让机器人通过迷宫

编辑
第一:谢谢你的回答

我问题的第二部分是:如果我们有一个多维图,该怎么办?是否有专门的实践,或者贾斯汀L.的答案也适用于此?
我认为这不是解决这个问题的最好办法

第三个问题:

以下哪种迷宫求解算法最快?(完全假设)

用矩阵构建一个图形,使用广度优先搜索、深度优先搜索或Dijkstras算法怎么样?

有很多迷宫求解算法:


对于机器人来说,它看起来很有前途。

你可以把迷宫想象成一棵树

A / \ / \ B C / \ / \ D E F G / \ \ H I J / \ L M / \ ** O (which could possibly represent) START + +---+---+ | A C G | +---+ + + + | D B | F | J | +---+---+ +---+---+ | L H E I | +---+ +---+---+ | M O | + +---+ FINISH (ignoring left-right ordering on the tree) 请注意,当迷宫中有“循环”时,这种方法只会变得稍微复杂一点(即,如果没有回溯,您可以重新进入已经穿过的通道)。检查评论,寻找一个好的解决方案

现在,让我们看看您提到的第一个解决方案,应用于这棵树

您的第一个解决方案基本上是a,这真的没有那么糟糕。这实际上是一个非常好的递归搜索。基本上,它说,“总是先采取最右边的方法。如果没有任何东西,回溯到第一个你可以直行或左行的地方,然后重复

深度优先搜索将按以下顺序搜索上述树:

A B D (backtrack) E H L (backtrack) M ** (backtrack) O (backtrack thrice) I
(backtrack thrice) C F (backtrack) G J
A (next level) B C (next level) D E F G (next level)
H I J (next level) L M (next level) ** O
请注意,您可以在找到**后立即停止

但是,当您实际编写深度优先搜索代码时,使用递归编程可以使一切变得更加简单。即使迭代方法也可以工作,而且您永远不必显式编程如何回溯。请查看链接文章以了解实现方法

另一种搜索树的方法是解决方案,它按深度搜索树。它将按以下顺序搜索上面的树:

A B D (backtrack) E H L (backtrack) M ** (backtrack) O (backtrack thrice) I
(backtrack thrice) C F (backtrack) G J
A (next level) B C (next level) D E F G (next level)
H I J (next level) L M (next level) ** O
请注意,由于迷宫的性质,宽度优先的平均检查节点数要高得多。宽度优先很容易实现,它有一个路径队列进行搜索,每次迭代都会从队列中弹出一条路径,“分解它”“通过在一个步骤后获取它可以转换为的所有路径,并将这些新路径放在队列的末尾。代码中没有明确的“下一级”命令,这些命令只是为了帮助理解

事实上,有一个整体。我刚才提到了两种最简单、最直接的方法

如果你的迷宫非常、非常长、很深,有循环和疯狂,而且很复杂,我建议使用这种算法,这是一种行业标准的寻路算法,它将广度优先搜索与启发式相结合……有点像“智能广度优先搜索”

它基本上是这样工作的:

  • 在队列中放置一条路径(您只需直接走一步进入迷宫的路径)。路径的“权重”由其当前长度+其到末端的直线距离(可通过数学计算)给出
  • 从队列中弹出权重最低的路径
  • 在一步之后,将路径“分解”为每一条路径。(即,如果您的路径为右-左-右,则您的分解路径为R L R和R L R L,不包括穿过墙壁的非法路径)
  • 如果其中一条路有目标,那就是胜利!否则:
  • 计算分解路径的权重,并将它们全部放回队列(不包括原始路径)
  • 按权重对队列排序,最低优先。然后重复步骤2
  • 这就是A*,我特别强调了它,因为它或多或少是用于所有寻路应用的行业标准寻路算法,包括从地图的一个边缘移动到另一个边缘,同时避免越野路径或山脉,它工作得很好,因为它使用了一个尽可能短的距离启发式,这给了它“智能”。A*是如此的通用,因为在给定任何问题的情况下,如果你有一个尽可能短的距离启发式(我们的很简单——直线),你可以应用它

    但是值得注意的是,*并不是你唯一的选择

    事实上,单是名单就有97个!(最好的仍将在前面链接)


    抱歉,长度=p(我倾向于漫无边际)

    这是我最喜欢的算法之一

    1) Move forward
    2) Are you at a wall?
    2a) If yes, turn left
    3) Are you at the finish?
    3a) If no, go to 1
    3b) If yes, solved
    

    一个有趣的方法,至少我觉得很有趣,就是使用细胞自动机。简言之,由3个“墙”单元包围的“空间”单元将变成“墙”单元。最后剩下的空间单元是通往出口的路径上的单元


    如果你看一看贾斯汀在他的答案里写的树,你会发现叶子节点有三堵墙。修剪这棵树,直到你有一条路。

    只是个主意。为什么不以蒙特卡洛的方式扔一些机器人进去呢。 让我们称第一代机器人为gen0。 我们只保留gen0中的机器人,这些机器人具有以下连续道路:
    -从头到尾
    或者——从某一点到最后

    我们在new ran中运行了新一代机器人
    While Not At End
        If Can Go North
            Go North
        ElseIf Can Go East
            Go East
        ElseIf Can Go South
            Go South
        ElseIf Can Go West 
            Go West
        EndIf
    Wend
    
    SXXXXXXXXXXXXX
       X         X
       X         X
       X         X
     XXX         X
     X X         X
     X XXXXXXXXXXX     XXXE
     X                 X
     XXXXXXXXXXXXXXXXXXX
    
    #ifndef vAlgorithms_Interview_graph_maze_better_h
    #define vAlgorithms_Interview_graph_maze_better_h
    
    static const int kMaxRows = 100;
    static const int kMaxColumns = 100;
    
    class MazeSolver
        {
    private:
        char m_matrix[kMaxRows][kMaxColumns]; //matrix representation of graph
        int rows, cols; //actual rows and columns
    
        bool m_exit_found;
        int m_exit_row, m_exit_col;
        int m_entrance_row, m_entrance_col;
    
        struct square //abstraction for data stored in every verex
            {
            pair<int, int> m_coord; //x and y co-ordinates of the matrix
            square* m_parent; //to trace the path backwards
    
            square() : m_parent(0) {}
            };
    
        queue<square*> Q;
    
    public:
        MazeSolver(const char* filename)
            : m_exit_found(false)
            , m_exit_row(0)
            , m_exit_col(0)
            , m_entrance_row(0)
            , m_entrance_col(0)
            {
            ifstream file;
            file.open(filename);
    
            if(!file)
                {
                cout << "could not open the file" << endl << flush;
                // in real world, put this in second phase constructor
                }
            init_matrix(file);
            }
        ~MazeSolver()
            {
            }
        void solve_maze()
            {
            //we will basically use BFS: keep pushing squares on q, visit all 4 neighbors and see
            //which way can we proceed depending on obstacle(wall)
    
            square* s = new square();
            s->m_coord = make_pair(m_entrance_row, m_entrance_col);
    
            Q.push(s);
    
            while(!m_exit_found && !Q.empty())
                {
                s = Q.front();
                Q.pop();
    
                int x = s->m_coord.first;
                int y = s->m_coord.second;
                //check if this square is an exit cell
                if(x == m_exit_row && y == m_exit_col)
                    {
                    m_matrix[x][y] = '>'; // end of the path
                    m_exit_found = true;
                    //todo: try breaking? no= queue wont empty
                    }
                else
                    {
                    //try walking all 4 neighbors and select best path
                    //NOTE: Since we check all 4 neighbors simultaneously,
                    //      the path will be the shortest path
                    walk_path(x-1, y, s);
                    walk_path(x+1, y, s);
                    walk_path(x, y-1, s);
                    walk_path(x, y+1, s);
                    }
                } /* end while */
    
            clear_maze(); //unset all previously marked visited shit
    
            //put the traversed path in maze for printing
            while(s->m_parent)
                {
                m_matrix[s->m_coord.first][s->m_coord.second] = '-';
                s = s->m_parent;
                } /* end while */
            }
    
        void print()
            {
            for(int i=0; i<rows; i++)
                {
                for(int j=0; j<cols; j++)
                    cout << m_matrix[i][j];
                cout << endl << flush;
                }
            }
    
    private:
        void init_matrix(ifstream& file)
            {
            //read the contents line-wise
            string line;
            int row=0;
            while(!file.eof())
                {
                std::getline(file, line);
                for(int i=0; i<line.size(); i++)
                    {
                    m_matrix[row][i] = line[i];
                    }
                row++;
                if(line.size() > 0)
                    {
                    cols = line.size();
                    }
                } /* end while */
            rows = row - 1;
    
            find_exit_and_entry();
            m_exit_found = false;
            }
    
        //find and mark ramp and exit points
        void find_exit_and_entry()
            {
            for(int i=0; i<rows; i++)
                {
                if(m_matrix[i][cols-1] == ' ')
                    {
                    m_exit_row = i;
                    m_exit_col = cols - 1;
                    }
                if(m_matrix[i][0] == ' ')
                    {
                    m_entrance_row = i;
                    m_entrance_col = 0;
                    }
                } /* end for */
            //mark entry and exit for testing
            m_matrix[m_entrance_row][m_entrance_col] = 's';
            m_matrix[m_exit_row][m_exit_col] = 'e';
            }
    
        void clear_maze()
            {
            for(int x=0; x<rows; x++)
                for(int y=0; y<cols; y++)
                    if(m_matrix[x][y] == '-')
                        m_matrix[x][y] = ' ';
            }
            // Take a square, see if it's the exit. If not, 
            // push it onto the queue so its (possible) pathways
            // are checked.
        void walk_path(int x, int y, square* parent)
            {
            if(m_exit_found) return;
            if(x==m_exit_row && y==m_exit_col)
                {
                m_matrix[x][y] = '>';
                m_exit_found = true;
                }
            else
                {
                if(can_walk_at(x, y))
                    {
                    //tag this cell as visited
                    m_matrix[x][y] = '-';
    
                    cout << "can walk = " << x << ", " << y << endl << flush;
    
                    //add to queue
                    square* s = new square();
                    s->m_parent = parent;
                    s->m_coord = make_pair(x, y);
                    Q.push(s);
                    }
                }
            }
    
        bool can_walk_at(int x, int y)
            {
            bool oob = is_out_of_bounds(x, y);
            bool visited = m_matrix[x][y] == '-';
            bool walled = m_matrix[x][y] == '#';
    
            return ( !oob && !visited && !walled);
            }
        bool is_out_of_bounds(int x, int y)
            {
            if(x<0 || x > rows || y<0 || y>cols)
                return true;
            return false;
            }
        };
    
    
    void run_test_graph_maze_better()
            {
            MazeSolver m("/Users/vshakya/Dropbox/private/graph/maze.txt");
            m.print();
            m.solve_maze();
            m.print();
            }
    
    
    #endif