在一个矩形数组中,我需要获得一个路径,该路径由一系列从一个最长的给定起点(单元格)开始的路径组成。我简单地搜索了什么是合适的搜索算法来实现这样的任务,并发现素数算法是最优选的技术。 它实际上是“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'的异常。
答案 0 :(得分:1)
在方法master
中替换条件
HEAD
通过
RecursiveTree
这会根据需要将搜索限制为值为1的单元格。
您的算法相当复杂且纠缠不清。此外,命名使得难以猜测事情的作用。例如,不清楚if (!cell.visited)
列举了什么。这个名字表明它是一种普遍的普查员;但是,if (!cell.visited && cell.value == 1)
仅返回EnumCell
的位置。为什么变量MoveNext()
总是value == 1
?为什么逻辑在results
和false
之间分开? 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)。