发明算法时我遇到了很大的问题。
我有两组节点。比如说,我们在第一组中有4个节点,在第二组中有另外4个节点。 下图说明了这一点:
我想将第一组中的节点与第二组中的节点连接起来。他们必须与最佳路径相连。 - 所有路线的长度必须尽可能接近,每个节点只能连接一条路线。 像这样:
我不想在下一张图片中这样做,因为路线的长度非常不同。
下表演示了所有节点之间的长度。从这张表中,我想选择最佳解决方案。最好的解决方案是环绕的。
当我拥有数百个节点的群组时,如何才能找到最佳解决方案? 如果我尝试每一个组合,就会有100个!组合,这是很多。 我不能为此发明任何算法。
答案 0 :(得分:3)
正如托马斯@在评论中所说,问题的解决方案取决于你对#34;的定义尽可能接近"。让我们将解决方案中的边集定义为S
。假设定义被选择为最小化max(S) - min(S)
(这在实践中是合理的假设),那么这个问题肯定可以在多项式时间内解决。
以下是该解决方案的一个示例。对于100个节点,它只需要几秒钟。
首先,您必须知道二分图的maximum matching problem,并且它可以在多项式时间内求解。最直接的Hungarian Algorithm需要O(nm)
,其中n
是节点数,m
是边数。对于您的情况,平面图there are more sophisticated algorithms that can further improve the performance。
让我们将其定义为函数Max_Match(X)
,它返回边集X
中的最大匹配数。
此Max_Match
可以小于O(n^1.5)
计算,其中n
是节点数。对于n=100
,我们只有n^1.5 = 1000
。
然后让我们将您的问题转换为最大匹配问题。
让我们为我们可以选择的所有边缘定义边E
的边集。在您的情况下,n*n
中有E
个边,其中n
是一边的节点数。
让我们定义功能
F(E, low, high) = {e | e in E and |e| >= low and |e| <= high }
这意味着E
的子集边,其长度介于[low, high]
然后,对于给定的一对数字low
和high
,当且仅当
<强> Max_Match( F (E, low, high)) == n
强>
但是,我们如何计算low
和high
的价值?
low
和high
的可能值是{|e|, e in E}
中包含n^2
个数字的所有可能数字。因此,尝试low
和high
的所有可能组合将花费n^4
。那很多。
如果我们已经知道low
,我们可以在没有枚举的情况下找出high
吗?答案是肯定的。请注意:
如果我们有一个h
号码
的 Max_Match( F (E, low, h)) == n
强>
然后对于每个号码h' >= h
我们也有
<强> Max_Match( F (E, low, h')) == n
强>
这意味着我们可以在修复high
后使用二进制搜索找出low
。
所以最终解决方案的框架:
arr[] = {|e|, e in E}.sort()
for i in range(0, len(arr[])):
low = i
h_left = i, h_right = len(arr[])-1
while h_left <= h_right:
mid = (h_left+h_right)/2
if Max_Match( F( E, arr[low], arr[mid]))==n:
h_right = mid - 1
else:
h_left = mid + 1
if h_left >= low:
if arr[h_left] - arr[low] <= ans:
ans = arr[h_left] - arr[low]
而ans
将是解决方案中边缘之间的最小差异。此算法的费用为O(n^3.5 * log(n))
,其中n
是节点数。
编辑:在c ++中可以找到以上算法的简单而难看的实现:
ideone.com/33n2Tg。因为我的时间不够,我在这个解决方案中手工制作了一个简单的匈牙利算法,当n
很大时,这个算法很慢。为了获得更好的性能,您需要第1部分中包含的算法用于平面图。
答案 1 :(得分:1)
我不认为你可以用A *轻松计算出来。蛮力也是O(n!)。我认为我们应该使用像遗传算法这样的启发式算法。
您可以使用数组实现gen(点A索引连接到值B点)
然后你必须生成一个含有随机基因的基因库。基因的数量没有固定......所以你必须测试一下。
您需要一种计算方法:
float GetLength(int indexA, int indexB)
{
return sqrt((pointA[indexA].x - pointB[indexB].x) * (pointA[indexA].x - pointB[indexB].x) + (pointA[indexA].y - pointB[indexB].y) * (pointA[indexA].y - pointB[indexB].y));
}
然后你还需要健身功能:
float Fitness(int gene, int count)
{
float min = GetLength(0,gene[0]);
float max = GetLength(0,gene[0]);
for(int i = 0; i< count;i++)
{
min = std::min(min, GetLength(i,gene[i]));
max = std::max(max, GetLength(i,gene[i]));
}
return max-min;
}
你还需要配对它们:
int* Crossbreed(int* gene1, int* gene2, int count)
{
int* geneNew = new int[count];
for(int i = 0; i< count; i++)
{
geneNew[gene1[i]] = gene2[i];
}
return geneNew;
}
杂交给了我们一些孩子;)必须通过健康测试。你也应该实现一个mutate方法,它给我们一个变异的基因。这对于保留局部最大值非常重要。
最后你只需要循环运行一段时间,你就会得到非常好的结果。
答案 2 :(得分:-1)
假设此表有n行和m列。 此问题相当于从此表中选择n个数字,它们位于不同的行和不同的列中,因此这些数字的总和最小。 我们可以使用dfs找到n中的所有组合!但是对于这个问题,我们可以有一个A* search algorithm。
这种A *算法的复杂性很难计算,但它经常表现良好。