如何使用prim算法遍历C#中矩形数组中的单元格

时间:2017-07-15 14:48:47

标签: c# algorithm search multidimensional-array tree-traversal

在一个矩形数组中,我需要获得一个路径,该路径由一系列从一个最长的给定起点(单元格)开始的路径组成。我简单地搜索了什么是合适的搜索算法来实现这样的任务,并发现素数算法是最优选的技术。 它实际上是“Prime算法”还是其他?

我尝试使用基于Dijkstra算法的标准递归,但我无法获得正确的路径。

以下代码适用于Windows窗体应用程序,有时会输出正确的结果,有时不会:

namespace AUV_Topology
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;


        /* Class Cell */
        /*****************************************************************************************************************************/

        public class Cell
        {
            public int row { get; set; }
            public int col { get; set; }
            public int value { get; set; }
            public Boolean visited { get; set; }
        }

        /* Class EnumCell  */
        /*****************************************************************************************************************************/

        public class EnumCell : IEnumerator<Cell>
        {
            public EnumCell() { }

            public EnumCell(int startRow, int startCol, List<List<Cell>> graph)
            {
                this.row = startRow;
                this.col = startCol;
                this.numberOfRows = graph.Count;
                this.numberOfCols = graph[0].Count;
                this.graph = graph;
            }

            public enum XY
            {
                Y = 0,  //row
                X = 1   //col
            }

            public enum LOCATIONS : byte
            {
                TOP_LEFT = 0,
                TOP_MIDDLE,
                TOP_RIGHT,
                LEFT,
                RIGHT,
                BOTTOM_LEFT,
                BOTTOM_MIDDLE,
                BOTTOM_RIGHT,
                END,
                INVALID
            }

            public List<List<Cell>> graph { get; set; }
            public int row { get; set; }
            public int col { get; set; }
            public int numberOfRows { get; set; }
            public int numberOfCols { get; set; }

            //offsets are in same order as enum location as y-offset(row), x-offset (col)
            private List<List<int>> offsets = new List<List<int>>() {
                    new List<int>() { -1, -1 },
                    new List<int>() { -1, 0 },
                    new List<int>() { -1, +1 },
                    new List<int>() { 0, -1 },
                    new List<int>() { 0, +1 },
                    new List<int>() { +1, -1 },
                    new List<int>() { +1, 0 },
                    new List<int>() { +1, +1 }
                };
            public LOCATIONS position { get; set; }

            public EnumCell GetEnumerator()
            {
                return new EnumCell(row, col, graph);
            }

            object IEnumerator.Current
            {
                get {
                    return Current;
                }
            }

            /* Class Current Cell  */
            /*****************************************************************************************************************************/
            public Cell Current
            {
                get {
                    try {
                        // move to first valie postion
                        for (LOCATIONS location = position; location < LOCATIONS.END; location++) {
                            if ((row + offsets[(byte)location][(int)XY.Y] >= 0) && (row + offsets[(byte)location][(int)XY.Y] < numberOfRows) &&
                                (col + offsets[(byte)location][(int)XY.X] > 0) && (col + offsets[(byte)location][(int)XY.X] < numberOfCols)) {
                                position = (LOCATIONS)location;
                                int newRow = row + offsets[(byte)location][(int)XY.Y];
                                int newCol = col + offsets[(byte)location][(int)XY.X];
                                return graph[newRow][newCol];
                            }
                        }
                        throw new InvalidOperationException();
                    } catch (IndexOutOfRangeException) {
                        throw new InvalidOperationException();
                    }
                }
            }

            public Boolean MoveNext()
            {
                Boolean results = false;
                for (LOCATIONS location = ++position; location < LOCATIONS.END; location++) {
                    int y = offsets[(byte)location][(int)XY.Y];
                    int x = offsets[(byte)location][(int)XY.X];
                    if ((row + y >= 0) && (row + y < numberOfRows) &&
                        (col + x > 0) && (col + x < numberOfCols)) {
                        if (graph[row + y][col + x].value == 1) {
                            position = (LOCATIONS)location;
                            return true;
                        }
                    }
                }
                return results;
            }

            public void Reset()
            {
                position = LOCATIONS.TOP_LEFT;
            }

            public void Dispose()
            {
            }
        }

        /* Class Graph  */
        /*****************************************************************************************************************************/
        public class Graph
        {
            public Graph(int[,] graph)
            {
                this.graph = new List<List<Cell>>();
                for (int row = 0; row < graph.GetLength(0); row++) {
                    List<Cell> newRow = new List<Cell>();
                    this.graph.Add(newRow);
                    for (int col = 0; col < graph.GetLength(1); col++) {
                        Cell newCell = new Cell();
                        newRow.Add(newCell);

                        newCell.row = row;
                        newCell.col = col;
                        newCell.value = graph[row, col];
                        newCell.visited = false;
                    }
                }
            }
            public List<List<Cell>> graph;
        }

        /* Class SpanningTree  */
        /*****************************************************************************************************************************/
        class SpanningTree
        {
            public static Graph graph = null;
            public static SpanningTree root = new SpanningTree();

            public static int counter = 0;

            public int row { get; set; }
            public int col { get; set; }
            public int length { get; set; }
            public List<SpanningTree> children { get; set; }

            public SpanningTree() { }
            public SpanningTree(int startRow, int startCol, int[,] graph)
            {
                SpanningTree.graph = new Graph(graph);
                RecursiveTree(root, SpanningTree.graph.graph[startRow][startCol]);
            }

            public int RecursiveTree(SpanningTree parent, Cell currentCell)
            {
                int length = 0;
                int maxLength = 0;
                parent.row = currentCell.row;
                parent.col = currentCell.col;

                graph.graph[currentCell.row][currentCell.col].visited = true;

                EnumCell enumCell = new EnumCell(currentCell.row, currentCell.col, graph.graph);
                foreach (Cell cell in enumCell) {
                    if (!cell.visited) {
                        SpanningTree newBranch = new SpanningTree();
                        if (parent.children == null) parent.children = new List<SpanningTree>();
                        parent.children.Add(newBranch);
                        length = RecursiveTree(newBranch, SpanningTree.graph.graph[cell.row][cell.col]);
                        if (length > maxLength) maxLength = length;
                    }
                }
                graph.graph[currentCell.row][currentCell.col].visited = false;

                parent.length = maxLength;
                return maxLength + 1;
            }

            public static void OrderHighLow(SpanningTree parent, int level)
            {
                if (parent.children != null) {
                    parent.children = parent.children.OrderByDescending(x => x.length).ToList();
                    foreach (SpanningTree child in parent.children) {
                        OrderHighLow(child, level + 1);
                    }
                }
            }

            public static void Print(SpanningTree parent, int level, int chromosomeNum)
            {
                FileStream fs = new FileStream("C:/Users/Welcome/Desktop/TreeParser.txt", FileMode.Append, FileAccess.Write);
                using (StreamWriter sw = new StreamWriter(fs)) {
                    sw.WriteLine("------------------- Chromosome : {0} -------------------", chromosomeNum);
                    Print(parent, level, sw);
                    sw.WriteLine("---------Longest----------");
                    PrintLongest(parent, level, sw);
                    counter = 0;
                }
            }

            private static void Print(SpanningTree parent, int level, StreamWriter sw)
            {
                //////////////////////////////////////////////////////////////////////
                sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
                //////////////////////////////////////////////////////////////////////

                if (parent.children != null) {
                    foreach (SpanningTree child in parent.children) {
                        Print(child, level + 1, sw);

                        if (child.length == 0) {
                            sw.WriteLine("||,,,,,,Branch {0},,,,,,||", counter);
                            level = 0;
                            sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, root.row, root.col, root.length);
                            counter += 1;
                        }
                    }
                }
            }

            public static void PrintLongest(SpanningTree parent, int level, StreamWriter sw)
            {
                //////////////////////////////////////////////////////////////////////
                sw.WriteLine("{0}Level : '{1}', Row : '{2}', Col : '{3}', Length : '{4}'", new string(' ', 4 * level), level, parent.row, parent.col, parent.length);
                //////////////////////////////////////////////////////////////////////

                if (parent.children != null) {
                    PrintLongest(parent.children[0], level + 1, sw);
                }
            }
        }

}// end name space

我以主要形式打电话:

 int tt = 0;

 foreach (int[,] graph in rectArrays)
 {
     new SpanningTree(indexesX[tt], indexesY[tt], graph);
     SpanningTree.OrderHighLow(SpanningTree.root, 0);
     SpanningTree.Print(SpanningTree.root, 0,tt);
     tt++;
 }

以下是最新结果:Debug

问题1

有时路径不会像预期的那样长,即某些情况(单元格)不应包括在内!有时路径包含值为零的单元格,有时它会从一个单元格移动到另一个单元格而它们不相邻彼此!。

问题2

当我使用9 x 9或更高的大矩阵时;我得到了以下内容:

  

抛出了类型'System.OutOfMemoryException'的异常。

1 个答案:

答案 0 :(得分:1)

在方法master中替换条件

HEAD

通过

RecursiveTree

这会根据需要将搜索限制为值为1的单元格。

您的算法相当复杂且纠缠不清。此外,命名使得难以猜测事情的作用。例如,不清楚if (!cell.visited) 列举了什么。这个名字表明它是一种普遍的普查员;但是,if (!cell.visited && cell.value == 1) 仅返回EnumCell的位置。为什么变量MoveNext()总是value == 1?为什么逻辑在resultsfalse之间分开? MoveNext()只应返回Current中找到的值。如果Current正常工作,则MoveNext中不需要其他逻辑。你的代码中还有很多其他的奇怪之处。 C#有一个yield return语句,这使得编写枚举器变得更加容易。

尝试简化代码并进行调试。

我实现了一个回溯解决方案,它返回所有具有最大路径长度的解决方案。除了设置和打印结果外,没有其他逻辑:

声明:

MoveNext

回溯算法(在路径的有效起点上调用Current):

private int[,] _world;
private bool[,] _visited;

[DebuggerDisplay("Coord({Row}, {Col})")]
private struct Coord
{
    public Coord(int row, int col)
    {
        this.Row = row;
        this.Col = col;
    }
    public readonly int Row;
    public readonly int Col;
}

请注意,完整结果树仅存在于调用堆栈中,而不是作为显式数据结构存在。路径(坐标列表)从路径端点向后创建到起点。

有了这个世界

Find

对于起始坐标(row = 0,column = 1),我的解决方案打印(打印例程本身未显示):

// Returns a list of paths of maximum length consisting of lists of coordinates.
private List<List<Coord>> Find(int row, int col)
{
    _visited[row, col] = true;
    var allResults = new List<List<Coord>>();
    for (int i = Math.Max(0, row-1); i <= Math.Min(_world.GetLength(0)-1, row+1); i++) {
        for (int j = Math.Max(0, col-1); j <= Math.Min(_world.GetLength(1)-1, col+1); j++) {
            if (!_visited[i, j] && _world[i, j] == 1) {
                List<List<Coord>> result = Find(i, j);
                allResults.AddRange(result);
            }
        }
    }
    if (allResults.Count == 0) {
        // This is an end-point of a path. Create the new path with current coord.
        // We construct the paths backward.
        allResults.Add(new List<Coord> { new Coord(row, col) });
    } else {
        // Keep only longest results
        int maxLength = allResults.Max(p => p.Count);
        for (int i = allResults.Count - 1; i >= 0; i--) {
            if (allResults[i].Count < maxLength) {
                allResults.RemoveAt(i);
            } else {
                // Prepend current point to path.
                allResults[i].Insert(0, new Coord(row, col));
            }
        }
    }
    _visited[row, col] = false;
    return allResults;
}

如果您注释掉丢弃较短结果的部分,您可以看到有8条可能的路径(长度为5的长度为4,长度为6的长度为4,长度为7的长度为2)。