选择数字矩形的数字

时间:2016-06-25 15:50:05

标签: algorithm

发明算法时我遇到了很大的问题。

我有两组节点。比如说,我们在第一组中有4个节点,在第二组中有另外4个节点。 下图说明了这一点:

Two groups of nodes

我想将第一组中的节点与第二组中的节点连接起来。他们必须与最佳路径相连。 - 所有路线的长度必须尽可能接近每个节点只能连接一条路线。 像这样:

enter image description here

我不想在下一张图片中这样做,因为路线的长度非常不同。

Bad connection between the nodes

下表演示了所有节点之间的长度。从这张表中,我想选择最佳解决方案。最好的解决方案是环绕的。

Table of lengths between nodes.

当我拥有数百个节点的群组时,如何才能找到最佳解决方案? 如果我尝试每一个组合,就会有100个!组合,这是很多。 我不能为此发明任何算法。

3 个答案:

答案 0 :(得分:3)

正如托马斯@在评论中所说,问题的解决方案取决于你对#34;的定义尽可能接近"。让我们将解决方案中的边集定义为S。假设定义被选择为最小化max(S) - min(S)(这在实践中是合理的假设),那么这个问题肯定可以在多项式时间内解决。

以下是该解决方案的一个示例。对于100个节点,它只需要几秒钟。

1

首先,您必须知道二分图的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

2

然后让我们将您的问题转换为最大匹配问题。

让我们为我们可以选择的所有边缘定义边E的边集。在您的情况下,n*n中有E个边,其中n是一边的节点数。

让我们定义功能 F(E, low, high) = {e | e in E and |e| >= low and |e| <= high } 这意味着E的子集边,其长度介于[low, high]

之间

然后,对于给定的一对数字lowhigh,当且仅当

时,您的问题才有解决方案

<强> Max_Match( F (E, low, high)) == n

3

但是,我们如何计算lowhigh的价值?

lowhigh的可能值是{|e|, e in E}中包含n^2个数字的所有可能数字。因此,尝试lowhigh的所有可能组合将花费n^4。那很多。

如果我们已经知道low,我们可以在没有枚举的情况下找出high吗?答案是肯定的。请注意:

引理1

如果我们有一个h号码 的 Max_Match( F (E, low, h)) == n

然后对于每个号码h' >= h我们也有

<强> Max_Match( F (E, low, h')) == n

这意味着我们可以在修复high后使用二进制搜索找出low

4

所以最终解决方案的框架:

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点)

enter image description here

然后你必须生成一个含有随机基因的基因库。基因的数量没有固定......所以你必须测试一下。

您需要一种计算方法:

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

对于这个问题,启发函数h(x)是 enter image description here

这种A *算法的复杂性很难计算,但它经常表现良好。