我想读取txt文件并以以下形式创建多维int数组:
var graph = new[,]
{
// 0 1 2 3 4 5 6 7 8 9 10 11...n
{ 0, 0, 0, 0, 0, 0, 10, 0, 12, 0, 0, 0 }, // 0
{ 0, 0, 0, 0, 20, 0, 0, 26, 0, 5, 0, 6 }, // 1
{ 0, 0, 0, 0, 0, 0, 0, 15, 14, 0, 0, 9 }, // 2
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0 }, // 3
{ 0, 20, 0, 0, 0, 5, 17, 0, 0, 0, 0, 11 }, // 4
{ 0, 0, 0, 0, 5, 0, 6, 0, 3, 0, 0, 33 }, // 5
{10, 0, 0, 0, 17, 6, 0, 0, 0, 0, 0, 0 }, // 6
{ 0, 26, 15, 0, 0, 0, 0, 0, 0, 3, 0, 20 }, // 7
{12, 0, 14, 0, 0, 3, 0, 0, 0, 0, 0, 0 }, // 8
{ 0, 5, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0 }, // 9
{ 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 }, // 10
{ 0, 6, 9, 0, 11, 33, 0, 20, 0, 0, 0, 0 }, // 11..n
};
这应该表示图中的节点,并且彼此之间存在距离。我的txt文件看起来像这样(图片和图形值不匹配):
第一个值是起点,第二个值是终点,第三个值应该是距离。 因此,如果节点之间没有直接连接,则表中行的索引为现有的起始节点,并且字段中的值应为0,如果存在直接连接,则该字段与txt文件的距离应为0。列的索引应该是所有末端节点。
我是这样开始的:
using (var reader = new StreamReader(@"USA-road-d.CAL.gr"))
{
while (!reader.EndOfStream)
{
var lineCount = File.ReadLines(@"USA-road-d.CAL.gr").Count();
var pre_graph = new int[lineCount];
//initialize array with 0s
Array.Clear(pre_graph, 0, pre_graph.Length);
string new_line;
while ((new_line = reader.ReadLine()) != null)
{
var values = new_line.Split(null);
pre_graph[int.Parse(values[2])] = int.Parse(values[3]);
}
}
}
var pre_graph = new int[lineCount];
是错误的,因为有多个边缘。我只希望将不同的起始节点数作为数组长度。
我不确定如何获得此信息。有人可以帮忙吗?
最后,我想将此图用于dijkstra算法的实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
public static class DijkstraWithoutQueue
{
public static List<int> DijkstraAlgorithm(int[,] graph, int sourceNode, int destinationNode)
{
var n = graph.GetLength(0);
var distance = new int[n];
for (int i = 0; i < n; i++)
{
distance[i] = int.MaxValue;
}
distance[sourceNode] = 0;
var used = new bool[n];
var previous = new int?[n];
while (true)
{
var minDistance = int.MaxValue;
var minNode = 0;
for (int i = 0; i < n; i++)
{
if (!used[i] && minDistance > distance[i])
{
minDistance = distance[i];
minNode = i;
}
}
if (minDistance == int.MaxValue)
{
break;
}
used[minNode] = true;
for (int i = 0; i < n; i++)
{
if (graph[minNode, i] > 0)
{
var shortestToMinNode = distance[minNode];
var distanceToNextNode = graph[minNode, i];
var totalDistance = shortestToMinNode + distanceToNextNode;
if (totalDistance < distance[i])
{
distance[i] = totalDistance;
previous[i] = minNode;
}
}
}
}
if (distance[destinationNode] == int.MaxValue)
{
return null;
}
var path = new LinkedList<int>();
int? currentNode = destinationNode;
while (currentNode != null)
{
path.AddFirst(currentNode.Value);
currentNode = previous[currentNode.Value];
}
return path.ToList();
}
public static void Main()
{
var graph = new[,]
{
// 0 1 2 3 4 5 6 7 8 9 10 11
{ 0, 0, 0, 0, 0, 0, 10, 0, 12, 0, 0, 0 }, // 0
{ 0, 0, 0, 0, 20, 0, 0, 26, 0, 5, 0, 6 }, // 1
{ 0, 0, 0, 0, 0, 0, 0, 15, 14, 0, 0, 9 }, // 2
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0 }, // 3
{ 0, 20, 0, 0, 0, 5, 17, 0, 0, 0, 0, 11 }, // 4
{ 0, 0, 0, 0, 5, 0, 6, 0, 3, 0, 0, 33 }, // 5
{10, 0, 0, 0, 17, 6, 0, 0, 0, 0, 0, 0 }, // 6
{ 0, 26, 15, 0, 0, 0, 0, 0, 0, 3, 0, 20 }, // 7
{12, 0, 14, 0, 0, 3, 0, 0, 0, 0, 0, 0 }, // 8
{ 0, 5, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0 }, // 9
{ 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 }, // 10
{ 0, 6, 9, 0, 11, 33, 0, 20, 0, 0, 0, 0 }, // 11
};
PrintPath(graph, 0, 2);
PrintPath(graph, 0, 10);
PrintPath(graph, 0, 11);
PrintPath(graph, 0, 1);
}
public static void PrintPath(int[,] graph, int sourceNode, int destinationNode)
{
Console.Write(
"Shortest path [{0} -> {1}]: ",
sourceNode,
destinationNode);
var path = DijkstraWithoutQueue.DijkstraAlgorithm(graph, sourceNode, destinationNode);
if (path == null)
{
Console.WriteLine("no path");
}
else
{
int pathLength = 0;
for (int i = 0; i < path.Count - 1; i++)
{
pathLength += graph[path[i], path[i + 1]];
}
var formattedPath = string.Join("->", path);
Console.WriteLine("{0} (length {1})", formattedPath, pathLength);
}
}
}
答案 0 :(得分:1)
我在GitHub上为您准备了一个解决方案,
使用1890815x1890815矩阵的数组上网不是一个好主意。 表示这样的阵列(1890815x1890815x4x2)大约需要28601450兆字节,或者您要注意具有28 PB内存的解决方案。
我想您将永远无法为美国路线图创建坚实的矩阵,因为,您可能需要大约1T的DDR内存(经过优化)或28 PB的数据(包括零),我为您准备了Visual Studio解决方案(VS2019预览版)需要打开项目):
这也是一个好消息,您可以创建和使用连接矩阵(C#8.0)从文件中加载数据,并在大约5秒钟内创建节点和边缘的树形结构,如果忘记了28 PB的要求。
5.6秒从存档中加载所有美国道路,并验证结构
static class Program
{
static async Task Main(string[] args)
{
using (var archive = ZipFile.OpenRead(args[0]))
{
var entry = archive.Entries.Where(_ => _.FullName.Equals("USA-road-d.CAL.gr", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (entry != null)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var data = new List<string>(Decompress(entry.Open()));
var graph = new Graph(data);
stopwatch.Watch();
Console.ReadLine();
}
}
}
public static IEnumerable<string> Decompress(Stream stream)
{
using (var reader = new StreamReader(stream, Encoding.ASCII))
{
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
public class Edge
{
public Edge(Graph graph, Node v1, Node v2, int distance, bool isDirectional = false)
{
Graph = graph;
V1 = v1;
V2 = v2;
Distance = distance;
v1.AddEdge(this);
if (!IsDirectional)
v2.AddEdge(this);
}
internal Graph Graph { get; }
public Node V1 { get; }
public Node V2 { get; }
public int Distance { get; }
public bool IsDirectional { get; }
}
public class Node
{
static int counter = 0;
readonly List<Edge> _edges = new List<Edge>();
public Node(Graph graph, int index)
{
Graph = graph;
Index = index;
Number = counter++;
}
internal Graph Graph { get; }
public int Index { get; }
public int Number { get; }
public IEnumerable<Edge> Edges => _edges;
public void AddEdge(Edge edge) => _edges.Add(edge);
}
public class Graph
{
Dictionary<int, Node> _nodes { get; } = new Dictionary<int, Node>();
readonly Parameters _parameters;
readonly List<Edge> _edges;
readonly List<string> _comments;
public IParameters Parameters => _parameters;
public IEnumerable<Edge> Edges => _edges;
public IEnumerable<string> Comments => _comments;
public Node GetNode(int index) => _nodes.ContainsKey(index) ? _nodes[index] : (_nodes[index] = new Node(this, index));
public Graph(IEnumerable<string> lines, bool isDirectional = false)
{
IEnumerable<string> parameters = new List<string>(lines.Where(_ => _[0] == 'p'));
IEnumerable<string> edges = new List<string>(lines.Where(_ => _[0] == 'a'));
IEnumerable<string> comments = new List<string>(lines.Where(_ => _[0] == 'c'));
_parameters =
parameters
.Select(_ => _.Split(' '))
.Select(_ => _[1] == "sp" ? new Parameters(int.Parse(_[2]), int.Parse(_[3])) : null).FirstOrDefault();
_edges =
edges
.Select(_ => _.Split(' '))
.Select(_ => new Edge(this, GetNode(int.Parse(_[1])), GetNode(int.Parse(_[2])), int.Parse(_[3]), isDirectional))
.ToList();
_comments =
comments
.Select(_ => _.Substring(1)).ToList();
if (_parameters.Nodes != _nodes.Keys.Count)
{
throw new Exception("invalid nodes count");
}
if (_parameters.Edges != _edges.Count)
{
throw new Exception("invalid edges count");
}
}
}
public interface IParameters
{
int Nodes { get; }
int Edges { get; }
}
public class Parameters: IParameters
{
public int Nodes { get; }
public int Edges { get; }
public Parameters(int nodes, int edges)
{
Nodes = nodes;
Edges = edges;
}
}
}
public static class StopwatchExtensions
{
public static void Watch(this Stopwatch stopwatch, string message = "",
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0) =>
Console.WriteLine(
$"{stopwatch.Elapsed} " +
$"{message} " +
$"{memberName} " +
$"{sourceFilePath}:{sourceLineNumber}");
}