试图寻找算法来找到从起始顶点通过所有图形顶点(无需返回起始边缘)的最快路径时,我几乎迷失了头脑。
我检查了图形的所有主要算法以及关于stackoverflow的类似问题。
我搜索过的几乎所有TSP示例都是完整的图形。
TSPLIB看起来无法解决我的问题。
对不起,如果我错过了什么。
•加权
•无向
•平面
•没有哈密尔顿路径
•没有消极边缘
•大小:最多100个顶点(但通常为50-70)
图中的边长几乎相同,因此可以说这是一个未加权的图,边长为1。
应通过“死胡同”解决:
实际输入图如下所示(从顶点0开始):
大图:
•从起始边缘到所有边缘的最短路径(一组顶点索引)。
•无需从末尾返回起始边缘
1)检查路径的所有可能组合,测量距离并找到距离最小的路径。
1a)使用深度优先搜索或宽度优先搜索
1b)如果在迭代当前顶点时有多个边缘,请对所有顶点进行单独组合(尝试所有可能的方式)。
1c)在我的情况下,图中有很多“死角”,因此算法应从那里找到方法(最快的ofc),并遍历已通过的顶点而不会卡住。
2)重建路径
也许我也应该在这里使用最小生成树算法...
或者也许为了更快的计算,我应该将我的图拆分为多个最小的图,这些图仅与单个边链接(如屏幕截图中的49-52、40-41)
任何帮助将不胜感激。
首选C#示例(库),但我可以移植任何语言的代码。
答案 0 :(得分:0)
我的情况下,应该尽快解决这个NP难题,而不是那么完美,所以我为我使用了最佳解决方案(简化版)(最佳情况是O(n + n * m),n -节点,m-边):
================================================ ====
使用分支绑定方式:
正如我在对问题的评论中提到的那样,我几乎以分支定界的方式解决了这个问题。这个想法是给每个排列和处理只给一个更大的分数。
如果有人感兴趣,这是示例代码:
using System.Collections.Generic;
using System.Linq;
using GraphFastPath.GraphData;
namespace GraphFastPath
{
public class BranchNBound
{
public static BranchNBound Instance;
private readonly Graph _graph;
public bool _ignoreDeadEnds;
public SortedDictionary<float, List<BbIterationStep>> _iterations = new SortedDictionary<float, List<BbIterationStep>>();
public List<BbIterationStep> BestPath = new List<BbIterationStep>();
public int IdCounter;
public int MaxNodesVisited;
public BbIterationStep PathNode;
public BranchNBound(Graph graph, bool ignoreDeadEnds)
{
_graph = graph;
_ignoreDeadEnds = ignoreDeadEnds;
Instance = this;
var nodesCount = ignoreDeadEnds ? _graph.Nodes.Count(x => !x.IsDeadEnd) : _graph.Nodes.Count;
foreach (var edge in _graph.Nodes[0].Edges)
AddStep(new BbIterationStep(edge, nodesCount), 1000);
}
public int IterationsCount => _iterations.Sum(x => x.Value.Count);
public void RegisterGoodPath(BbIterationStep step)
{
if (step._uniqPassedNodesCount < MaxNodesVisited)
return;
if (step._uniqPassedNodesCount > MaxNodesVisited)
{
BestPath.Clear();
MaxNodesVisited = step._uniqPassedNodesCount;
}
BestPath.Add(step);
}
public void DoStep()
{
var min = _iterations.Last();
var list = min.Value;
_iterations.Remove(min.Key);
foreach (var step in list)
step.DoStep();
}
public void AddStep(BbIterationStep step, float cost)
{
step.VariantId = IdCounter++;
if (!_iterations.TryGetValue(cost, out var list))
{
list = new List<BbIterationStep>();
_iterations.Add(cost, list);
}
list.Add(step);
}
}
public class BbIterationStep
{
private readonly int _totalNodesCount;
private readonly Edge _currentEdge;
private int _totalPassedNodesCount;
public int _uniqPassedNodesCount;
public string Debug;
public List<Node> LocalPath = new List<Node>();
public Node Node;
public BbIterationStep Parent;
public float Score;
public int VariantId;
public BbIterationStep(Edge currentEdge, int nodesCount)
{
_currentEdge = currentEdge;
_totalNodesCount = nodesCount;
Node = _currentEdge.From;
_uniqPassedNodesCount++;
_totalPassedNodesCount++;
}
public BbIterationStep(BbIterationStep from, Edge currentEdge, string debug, float score)
{
Parent = from;
Score = score;
_currentEdge = currentEdge;
Debug = debug;
Node = _currentEdge.From;
_uniqPassedNodesCount = from._uniqPassedNodesCount;
_totalPassedNodesCount = from._totalPassedNodesCount;
_totalNodesCount = from._totalNodesCount;
}
public int Id => _currentEdge.From.Id;
public Node NodeTo => _currentEdge.To;
public void DoStep()
{
_currentEdge.Pass(false);
_currentEdge.To.SetProcessed();
var passed = CheckPassed(_currentEdge.To);
if (!passed)
{
_uniqPassedNodesCount++;
if (BranchNBound.Instance.MaxNodesVisited < _uniqPassedNodesCount)
BranchNBound.Instance.RegisterGoodPath(this);
if (_uniqPassedNodesCount == _totalNodesCount)
BranchNBound.Instance.PathNode = this;
}
_totalPassedNodesCount++;
var totalDistFromStartMin = float.MaxValue;
var totalDistFromStartMax = float.MinValue;
foreach (var edge in _currentEdge.To.Edges)
{
var dist = edge.To.DistanceFromStart;
var nextNodePassedCount = CountPassed(edge.To);
if (nextNodePassedCount > 0)
dist *= nextNodePassedCount + 2;
if (totalDistFromStartMin > dist)
totalDistFromStartMin = dist;
if (totalDistFromStartMax < dist)
totalDistFromStartMax = dist;
}
var delta = totalDistFromStartMax - totalDistFromStartMin;
if (delta == 0)
delta = totalDistFromStartMax;
foreach (var edge in _currentEdge.To.Edges)
{
if (BranchNBound.Instance._ignoreDeadEnds && edge.To.IsDeadEnd)
continue;
var deltaGoodFromStart = 1 - (edge.To.DistanceFromStart - totalDistFromStartMin) / delta; // + float.Epsilon;
if (float.IsNaN(deltaGoodFromStart))
{
var test = 1;
}
MoveNextEdge(edge, deltaGoodFromStart);
}
}
private void MoveNextEdge(Edge edge, float deltaGoodFromStart)
{
var nextNodePassedCount = CountPassed(edge.To);
var progressScale = (float) _uniqPassedNodesCount / _totalNodesCount; //weight 200 :Total path search progress (how much it is completed/finished)
var nextNodeScoreScale = 1f / (nextNodePassedCount * nextNodePassedCount + 1); //weight 200 :Lower value if next node was visited more times
var pc = _uniqPassedNodesCount;
if (nextNodePassedCount == 0)
pc++;
var pathNoRepeatedNodesScoreScale = (float) pc / (_totalPassedNodesCount + 1); //weight 400 :Higher value if all nodes was visited less times
var progressScaleValue = progressScale * 1;
var nextNodeScoreValue = nextNodeScoreScale * 300;
var deltaGoodFromStartValue = deltaGoodFromStart * 500 * nextNodeScoreScale;
var pathNoRepeatedNodesScoreValue = pathNoRepeatedNodesScoreScale * 800;
//iterations with bigger score will be processed earlier
var totalScore = progressScaleValue +
nextNodeScoreValue +
deltaGoodFromStartValue +
pathNoRepeatedNodesScoreValue;
var dbg = $"Progress: {progressScaleValue} NextNode({edge.To.Id}): {nextNodeScoreValue} GoodStart: {deltaGoodFromStartValue} NoRepeat: {pathNoRepeatedNodesScoreValue}";
var newStep = new BbIterationStep(this, edge, dbg, totalScore);
BranchNBound.Instance.AddStep(newStep, totalScore);
}
public bool CheckPassed(Node node)
{
var checkStep = this;
do
{
if (checkStep.Node == node)
return true;
checkStep = checkStep.Parent;
} while (checkStep != null);
return false;
}
public void GetPathEdges(List<Edge> result)
{
var checkStep = this;
do
{
result.Add(checkStep._currentEdge);
checkStep = checkStep.Parent;
} while (checkStep != null);
}
private int CountPassed(Node node)
{
var passedCount = 0;
var checkStep = this;
do
{
if (checkStep.Node == node)
passedCount++;
checkStep = checkStep.Parent;
} while (checkStep != null);
return passedCount;
}
public override string ToString()
{
return Parent == null ? Id.ToString() : $"({Score}) ({VariantId}), {Debug}";
}
public string GetPath()
{
return Parent == null ? Id.ToString() : $"{Parent.GetPath()}-{Id}";
}
}
}
最有趣的部分是MoveNextEdge函数,该函数计算每个排列的得分。