从3d空间中的一组点移动到具有最短累积距离的另一组点

时间:2019-02-15 00:11:36

标签: c# algorithm graph-theory graph-algorithm graph-traversal

我们有2个列表(黑色和红色),每个列表在3d空间中包含多个点。我们必须将每个黑色点移动到一个红色点,并以使移动的总距离最小的方式进行操作。列表的大小可以不同。

2D空间中的简单正确解法: Correct Example

错误的解决方案:Incorrect solution


如果列表的大小不同,那么我们要么将点堆叠在一起,要么将一个点拆分成多个点。

拆分示例:Splitting example

堆叠示例:Stacking example


我们针对此问题的最佳尝试遵循以下一般步骤:

  1. 如果红色点多于黑色点,请选择距离所有红色点最远的黑色点,并将其与最接近其位置且尚未匹配的红色点匹配。

  2. p>
  3. 重复步骤1,直到所有黑点都匹配。

  4. 遍历剩余的红色点,并将每个红色点匹配到它们各自最近的黑色点,从而将它们堆叠在一起。结果将如下所示:enter image description here

  5. 注意:如果黑点多于红点,则第一步将查找最远的红点,并将其与最接近的黑点匹配,并继续进行相同的替换颜色。

一些C#代码:

private void SaveFrames(List<List<Vector3>> frameList) {
        List<Dictionary<Vector3, List<Vector3>>> resultingPairs = new List<Dictionary<Vector3, List<Vector3>>>();
        for (int iFrame = 0; iFrame < frameList.Count+1; iFrame++) {
            List<Vector3> currentFrame = frameList[iFrame % frameList.Count];
            List<Vector3> nextFrame = frameList[(iFrame + 1) % frameList.Count];

            int maxIterations = Mathf.Min(currentFrame.Count, nextFrame.Count);
            Dictionary<Vector3, List<Vector3>> pairs = new Dictionary<Vector3, List<Vector3>>();
            HashSet<Vector3> takenRed = new HashSet<Vector3>();
            HashSet<Vector3> takenBlack = new HashSet<Vector3>();
            HashSet<Vector3> takenDestination = new HashSet<Vector3>();
            bool moreRed = currentFrame.Count < nextFrame.Count;

            if (moreRed) {
                for (int i = 0; i < maxIterations; i++) {
                    // Find furthest black point from any red point
                    float distance = 0;
                    Vector3 furthestBlack = Vector3.zero;
                    foreach (Vector3 black in currentFrame) {
                        if (takenBlack.Contains(black)) continue;
                        foreach (var red in nextFrame) {
                            if (Vector3.Distance(black, red) > distance) {
                                distance = Vector3.Distance(black, red);
                                furthestBlack = black;
                            }
                        }
                    }

                    // Find the closest red point to the furthest black point
                    distance = float.MaxValue;
                    Vector3 closestRed = Vector3.zero;
                    foreach (var red in nextFrame) {
                        if (takenRed.Contains(red)) continue;
                        if (Vector3.Distance(furthestBlack, red) < distance) {
                            distance = Vector3.Distance(furthestBlack, red);
                            closestRed = red;
                        }
                    }

                    if (!pairs.ContainsKey(furthestBlack)) {
                        pairs[furthestBlack] = new List<Vector3>();
                    }

                    if (!takenDestination.Contains(closestRed)) {
                        pairs[furthestBlack].Add(closestRed);
                        takenBlack.Add(furthestBlack);
                        takenRed.Add(closestRed);
                        takenDestination.Add(closestRed);
                    }

//                    Debug.Log("Pair: " + furthestBlack.ToString() + " to " + closestRed.ToString());
                }
            } else {
                for (int i = 0; i < maxIterations; i++) {
                    // Find furthest red point from any black point
                    float distance = 0;
                    Vector3 furthestRed = Vector3.zero;
                    foreach (Vector3 red in nextFrame) {
                        if (takenRed.Contains(red)) continue;
                        foreach (Vector3 black in currentFrame) {
                            if (Vector3.Distance(black, red) > distance) {
                                distance = Vector3.Distance(black, red);
                                furthestRed = red;
                            }
                        }
                    }

                    // Find the closest black point to the furthest red point
                    distance = float.MaxValue;
                    Vector3 closestBlack = Vector3.zero;
                    foreach (var black in currentFrame) {
                        if (takenBlack.Contains(black)) continue;
                        if (Vector3.Distance(furthestRed, black) < distance) {
                            distance = Vector3.Distance(furthestRed, black);
                            closestBlack = black;
                        }
                    }

                    if (!pairs.ContainsKey(closestBlack)) {
                        pairs[closestBlack] = new List<Vector3>();
                    }

                    if (!takenDestination.Contains(furthestRed)) {
                        pairs[closestBlack].Add(furthestRed);
                        takenBlack.Add(closestBlack);
                        takenRed.Add(furthestRed);
                        takenDestination.Add(furthestRed);
                    }

//                    Debug.Log("Pair: " + closestBlack.ToString() + " to " + furthestRed.ToString());
                }
            }




            if (currentFrame.Count < nextFrame.Count) {
                // For every nextFrame[i], find the closest black point and pair it.
                for (int i = currentFrame.Count; i < nextFrame.Count; i++) {
                    float distance = float.MaxValue;
                    Vector3 closestBlack = Vector3.zero;
                    foreach (var black in currentFrame) {
                        if (Vector3.Distance(nextFrame[i], black) < distance) {
                            distance = Vector3.Distance(nextFrame[i], black);
                            closestBlack = black;
                        }
                    }

                    if (!pairs.ContainsKey(closestBlack)) {
                        pairs[closestBlack] = new List<Vector3>();
                    }

                    if (!takenDestination.Contains(nextFrame[i])) {
                        pairs[closestBlack].Add(nextFrame[i]);
                        takenDestination.Add(nextFrame[i]);
                    }

//                    Debug.Log("Pair: " + closestBlack.ToString() + " to " + nextFrame[i].ToString());
                }
            }


            if (currentFrame.Count > nextFrame.Count) {
                // For every currentFrame[i], find the closest red point and pair it.
                for (int i = nextFrame.Count; i < currentFrame.Count; i++) {
                    float distance = float.MaxValue;
                    Vector3 closestRed = Vector3.zero;
                    foreach (var red in nextFrame) {
                        if (Vector3.Distance(currentFrame[i], red) < distance) {
                            distance = Vector3.Distance(currentFrame[i], red);
                            closestRed = red;
                        }
                    }

                    if (!pairs.ContainsKey(currentFrame[i])) {
                        pairs[currentFrame[i]] = new List<Vector3>();
                    }

                    if (!takenDestination.Contains(closestRed)) {
                        pairs[currentFrame[i]].Add(closestRed);
                        takenDestination.Add(closestRed);
                    }

//                    Debug.Log("Pair: " + currentFrame[i].ToString() + " to " + closestRed.ToString());
                }
            }
            resultingPairs.Add(pairs);
        }
}

此方法适用于简单的形状,例如立方体。 enter image description here

但是,当多维数据集的位置在3d空间中从一组点到另一个点重叠时,它开始起作用。

enter image description here

它甚至可以处理更复杂的问题: enter image description here

我不确定为什么会导致这种情况崩溃,并且我无法提出一个简单的二维示例来说明这种方法的错误之处。

我们已经在3天的很长的时间内尝试了3种不同的方法,但似乎找不到解决这个看似简单的问题的方法。

2 个答案:

答案 0 :(得分:1)

您可以将其解释为the Assignment problem,其中黑点是“代理”,红点是“任务”(反之亦然),它们之间的距离是成本。

  

问题实例具有许多代理和许多任务。可以分配任何座席以执行任何任务,这会产生一些费用,该费用可能会因座席任务分配而异。需要执行所有任务,方法是为每个任务分配一个代理,为每个代理分配一个任务,以使分配的总成本最小化。

可以使用The Hungarian algorithm在多项式时间内解决分配问题。问题的变体涉及more tasks than agents,您可以将其应用于列表大小不同的特殊情况。

答案 1 :(得分:0)

如果您想要一个“快速的'肮脏的”解决方案,应该给出不错的结果,请考虑将您当前的算法改编为一种概率算法。根据与黑点之间的距离,对每个附近的红点进行加权,然后根据其权重随机选择一个。您可能希望平方(或什至立方)距离,以防止选择更远的点。总的来说,它应该选择许多与原始算法相同的点,但是在这里和那里有一些差异。重复尽可能多的次数,然后选择最佳结果。

如果您希望减少一些麻烦,可以考虑将问题建模为非对称旅行推销员问题。将每个黑点连接到每个红点,并使其权重方向的权重与其之间的欧式距离成正比。然后将每个红色点连接到每个黑色点,并以权重0的方向边缘进行连接。然后使用现有的非对称TSP求解器进行求解,然后添加多余的节点+如有必要,正常连接。但是请注意,这将丢弃许多有用的信息(例如,我们并不特别关心下一个连接的黑色节点),以换取能够使用经过尝试和证明的启发式方法和优化方法的现有软件。 / p>