[迷宫中的算法实践]迷宫生成算法——Prim算法

       普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。

——来自百度百科

当我们将Prim算法用于迷宫生成时,情况有些不同,维基百科中给出了随机Prim迷宫生成算法的解释及实现过程:

Randomized Prim‘s algorithm

This algorithm is a randomized version of Prim‘s algorithm.

  1. Start with a grid full of walls.
  2. Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.
  3. While there are walls in the list:
    1. Pick a random wall from the list. If the cell on the opposite side isn‘t in the maze yet:

      1. Make the wall a passage and mark the cell on the opposite side as part of the maze.
      2. Add the neighboring walls of the cell to the wall list.
    2. Remove the wall from the list.

It will usually be relatively easy to find the way to the starting cell, but hard to find the way anywhere else.

Note that simply running classical Prim‘s on a graph with random edge weights would create mazes stylistically identical to Kruskal‘s, because they are both minimal spanning tree algorithms. Instead, this algorithm introduces stylistic variation because the edges closer to the starting point have a lower effective weight.

我们将算法实现部分翻译成中文

  1. 让迷宫全都是墙.。
  2. 选一个格,作为迷宫的通路,然后把它的邻墙放入列表.。
  3. 当列表里还有墙时:
    1. 从列表里随机选一个墙,如果它对面的格子不是迷宫的通路:

      1. 把墙打通,让对面的格子成为迷宫的通路.。
      2. 把那个格子的邻墙加入列表。
    2. 如果对面的格子已经是通路了,那就从列表里移除这面墙。

 

       简单研究算法实现过程我们可以发现,Prim算法就是不断地从所有可以是通路的位置中随意选一个挖洞,直到没有可能为通路的位置。

       整个实现过程还是相当于随意为路线附权值的Prim算法。

 

下面我们来做C#下的代码实现:

/// <summary>
/// 普利姆迷宫生成法
/// </summary>
/// <param name="startX">起始点X坐标</param>
/// <param name="startY">起始点Y坐标</param>
/// <param name="widthLimit">迷宫宽度</param>
/// <param name="heightLimit">迷宫高度</param>
/// <param name="haveBorder">迷宫是否含有墙</param>
private int[,] Prim(int startX, int startY, int widthLimit, int heightLimit,bool haveBorder)
{
    //block:不可通行    unBlock:可通行
    const int block = 0,unBlock = 1;
    var r=new Random();
    //迷宫尺寸合法化
    if (widthLimit < 1)
        widthLimit = 1;
    if (heightLimit < 1)
        heightLimit = 1;
    //迷宫起点合法化
    if (startX < 0 || startX >= widthLimit)
        startX = r.Next(0, widthLimit);
    if (startY < 0 || startY >= heightLimit)
        startY = r.Next(0, heightLimit);
    //减去边框所占的格子
    if (!haveBorder)
    {
        widthLimit--;
        heightLimit--;
    }
    //迷宫尺寸换算成带墙尺寸
    widthLimit *= 2;
    heightLimit *= 2;
    //迷宫起点换算成带墙起点
    startX *= 2;
    startY *= 2;
    if (haveBorder)
    {
        startX++;
        startY++;
    }
    //产生空白迷宫
    var mazeMap = new int[widthLimit + 1, heightLimit + 1];
    for (int x = 0; x <= widthLimit; x++)
    {
        //mazeMap.Add(new BitArray(heightLimit + 1));
        for (int y = 0; y <= heightLimit; y++)
        {
            mazeMap[x, y] = block;
        }
    }

    //邻墙列表
    var blockPos = new List<int>();
    //将起点作为目标格
    int targetX = startX, targetY = startY;
    //将起点标记为通路
    mazeMap[targetX, targetY] = unBlock;

    //记录邻墙
    if (targetY > 1)
    {
        blockPos.AddRange(new int[] { targetX, targetY - 1, 0 });
    }
    if (targetX < widthLimit)
    {
        blockPos.AddRange(new int[] { targetX + 1, targetY, 1 });
    }
    if (targetY < heightLimit)
    {
        blockPos.AddRange(new int[] { targetX, targetY + 1, 2 });
    }
    if (targetX > 1)
    {
        blockPos.AddRange(new int[] { targetX - 1, targetY, 3 });
    }
    while (blockPos.Count > 0)
    {
        //随机选一堵墙
        var blockIndex = r.Next(0, blockPos.Count / 3) * 3;
        //找到墙对面的墙
        if (blockPos[blockIndex + 2] == 0)
        {
            targetX = blockPos[blockIndex];
            targetY = blockPos[blockIndex + 1] - 1;
        }
        else if (blockPos[blockIndex + 2] == 1)
        {
            targetX = blockPos[blockIndex] + 1;
            targetY = blockPos[blockIndex + 1];
        }
        else if (blockPos[blockIndex + 2] == 2)
        {
            targetX = blockPos[blockIndex];
            targetY = blockPos[blockIndex + 1] + 1;
        }
        else if (blockPos[blockIndex + 2] == 3)
        {
            targetX = blockPos[blockIndex] - 1;
            targetY = blockPos[blockIndex + 1];
        }
        //如果目标格未连通
        if (mazeMap[targetX, targetY] == block)
        {
            //联通目标格
            mazeMap[blockPos[blockIndex], blockPos[blockIndex + 1]] = unBlock;
            mazeMap[targetX, targetY] = unBlock;
            //添加目标格相邻格
            if (targetY > 1 && mazeMap[targetX, targetY - 1] == block && mazeMap[targetX, targetY - 2] == block)
            {
                blockPos.AddRange(new int[] { targetX, targetY - 1, 0 });
            }
            if (targetX < widthLimit && mazeMap[targetX + 1, targetY] == block && mazeMap[targetX + 2, targetY] == block)
            {
                blockPos.AddRange(new int[] { targetX + 1, targetY, 1 });
            }
            if (targetY < heightLimit && mazeMap[targetX, targetY + 1] == block && mazeMap[targetX, targetY + 2] == block)
            {
                blockPos.AddRange(new int[] { targetX, targetY + 1, 2 });
            }
            if (targetX > 1 && mazeMap[targetX - 1, targetY] == block && mazeMap[targetX - 1, targetY] == block)
            {
                blockPos.AddRange(new int[] { targetX - 1, targetY, 3 });
            }
        }
        blockPos.RemoveRange(blockIndex, 3);
    }
    return mazeMap;
}

时间: 2024-10-09 12:26:24

[迷宫中的算法实践]迷宫生成算法——Prim算法的相关文章

最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小支撑树(minimum spanning tree)算法.给定一个无向图G,并且它的每条边均权值,则MST是一个包括G的所有顶点及边的子集的图,这个子集保证图是连通的,并且子集中所有边的权值之和为所有子集中最小的. 本节中介绍三种算法求解图的最小生成树:Prim算法.Kruskal算法和Boruvk

[迷宫中的算法实践]关于一个数据结构与算法实践作业的总结

最近听闻数据结构与算法实践课的老师又出了和上年一样的选题,不禁想起了去年自己完成作业时的点点滴滴,遗憾当时没有写博客的习惯,之前的一些心得这一年实践的过去也逐渐淡忘了,突然就有了总结一下的想法,希望能有新的收获吧. 由于当时也没注意保存,软件完成过程中的一些文档早已丢失了,幸运的是Winform版源码还在,Unity3D版程序也还幸存,虽然由于时间紧张只完成了大概框架,但美观程度也远非Winform可以相比的,先上几张软件图吧: 生成算法 软件实现了普利姆算法.递归回溯算法.递归分割算法和深度遍

贪心算法-最小生成树Kruskal算法和Prim算法

Kruskal算法: 不断地选择未被选中的边中权重最轻且不会形成环的一条. 简单的理解: 不停地循环,每一次都寻找两个顶点,这两个顶点不在同一个真子集里,且边上的权值最小. 把找到的这两个顶点联合起来. 初始时,每个顶点各自属于自己的子集合,共n个子集合. 每一步操作,都会将两个子集合融合成一个,进而减少一个子集合. 结束时,所有的顶点都在同一个子集合里,这个子集合就是最小生成树. 例子: 伪代码: Prim算法: G=(V,E),S是V的真子集,如果u在S中,v在V-S中,且(u,v)是图的一

算法设计和分析(Prim算法构建最小生成树)

问题: 给定无向图G(N,M)表明图G有N个顶点,M条边,通过Prim算法构造一个最小生成树 分析: 算法流程: 构造好的最小生成树就是step6 运行代码: #include<cstdio> #include<string.h> #include<algorithm> #include<cmath> #include<iostream> #include<vector> #include<queue> #include&

Prim算法(一)之 C语言详解

本章介绍普里姆算法.和以往一样,本文会先对普里姆算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 普里姆算法介绍 2. 普里姆算法图解 3. 普里姆算法的代码说明 4. 普里姆算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 普里姆算法介绍 普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法. 基本思想 对于图G而言,V是所

最小生成树 Prim算法 Kruskal算法

最小生成树 给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成树,如果边上有权值,那么使得边权和最小的生成树叫做最小生成树. 常见的求解最小生成树的算法有Kruskal算法和Prim算法,生成树是否存在和图是否连通是等价的,所以假定图是连通的. Prim算法 假设有一棵只包含一个顶点v的数T,然后贪心地选取T和其他顶点之间相连的最小权值的边,并把它加到T中.不断进行这个操作,就可以得到最小生成树了(可用反证法证明) 不使用Heap优化的代码 int eg[

Prim算法(三)之 Java详解

前面分别通过C和C++实现了普里姆,本文介绍普里姆的Java实现. 目录 1. 普里姆算法介绍 2. 普里姆算法图解 3. 普里姆算法的代码说明 4. 普里姆算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 普里姆算法介绍 普里姆(Prim)算法,是用来求加权连通图的最小生成树的算法. 基本思想 对于图G而言,V是所有顶点的集合:现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最

最小生成树之Kruskal算法和Prim算法

依据图的深度优先遍历和广度优先遍历,能够用最少的边连接全部的顶点,并且不会形成回路. 这样的连接全部顶点并且路径唯一的树型结构称为生成树或扩展树.实际中.希望产生的生成树的全部边的权值和最小,称之为最小生成树. 常见的最小生成树算法有Kruskal算法和Prim算法. Kruskal算法每次选取权值最小的边.然后检查是否增加后形成回路,假设形成回路则须要放弃.终于构成最小生成树.n个顶点的图最小生成树过程例如以下: 边的权值升序排序. 选取全部未遍历的边中权值最小的边,推断增加后是否形成回路,若

图的最小生成树(二)—Prim算法

上一篇中写了图的最小生成树求法一--Kruskal算法 http://blog.csdn.net/wtyvhreal/article/details/43526695 这一篇中用另外一种方法来求解图的最小生成树,Prim算法. 图中随便选一个顶点开始,看看这个顶点有哪些边,在它的边中找一条最短的.1号有1-2,1-3,其中1-2短,选择1-2.通过它把1和2连接在一起.接下来开始枚举1和2号顶点所有的边,看看哪些边可以连接到没有被选中的顶点,并且边越短越好. Prim算法的基本思路: 将图中的所