简体   繁体   English

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

[英]Moving from one set of points in 3d space to another set of points with the shortest possible accumulative distance

We have 2 lists(black and red), each one contains multiple points in 3d space. 我们有2个列表(黑色和红色),每个列表包含3d空间中的多个点。 We have to move each black point to a red point, and do this in such a way that the total distance to make the moves is the least it can be. 我们必须将每个黑点移动到一个红点,并以这样的方式进行,即移动的总距离是最小的。 The lists can be of different sizes. 列表可以具有不同的大小。

Simple correct soltution in 2D space: 在2D空间中简单正确的溶解: 正确的例子

Incorrect solution: 解决方案不正确: 解决方案不正确


If the sizes of the lists differ, then we have to either stack points on top of each other or split one point into multiple points. 如果列表的大小不同,那么我们必须将点堆叠在彼此之上或将一个点分成多个点。

Splitting example: 拆分示例: 分裂的例子

Stacking example: 堆叠示例: 堆叠示例


Our best attempt at this problem follows these general steps: 我们对此问题的最佳尝试遵循以下一般步骤:

  1. If there are more red points than black points, pick the black point that's furthest from all of the red point and match it with a red point that is closest to its position and has not been matched yet. 如果红点多于黑点,请选择距离所有红点最远的黑点,并将其与最接近其位置且尚未匹配的红点匹配。

  2. Repeat step 1 until all of the black points are matched. 重复步骤1,直到匹配所有黑点。

  3. Iterate over the leftover red points and match each one to their respective closest black point, thus stacking them. 迭代剩余的红点并将每个红点与各自最近的黑点相匹配,从而将它们堆叠起来。 The result will look something like this: 结果将如下所示: 在此输入图像描述

  4. Note: if there is more black points than red points, then step one will look for the furthest red point and match it to its closest black point and proceed all the same with the colors swapped. 注意:如果黑点多于红点,则第一步将查找最远的红点并将其与最近的黑点匹配,并对交换的颜色进行相同的处理。

Some C# code: 一些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);
        }
}

This method works for simple shapes like cubes. 此方法适用于像立方体这样的简单形状。 在此输入图像描述

However, it starts acting up when the cube positions overlap in 3d space from ne set of points to another. 然而,当立方体位置在3d空间中从一组点到另一个点重叠时,它开始起作用。

在此输入图像描述

And it does even funkier stuff with more complex points: 它甚至可以用更复杂的点来制作更有趣的东西: 在此输入图像描述

I am not exactly sure why this breaks down and I could not come up with a simple 2D example of where this approach goes wrong. 我不确定为什么会出现故障,我无法想出这个方法出错的简单2D示例。

We have tried 3 different methods over 3 very long days, and can not seem to find a solution to this seemingly simple problem. 我们在3天的时间里尝试了3种不同的方法,似乎无法找到解决这个看似简单问题的方法。

You can interpret this as the Assignment problem , where the black points are the "agents", the red points are the "tasks" (or vice versa) and the distance between them is the cost. 您可以将此解释为分配问题 ,其中黑点是“代理”,红点是“任务”(反之亦然),它们之间的距离是成本。

The problem instance has a number of agents and a number of tasks. 问题实例有许多代理和许多任务。 Any agent can be assigned to perform any task, incurring some cost that may vary depending on the agent-task assignment. 可以分配任何代理来执行任何任务,从而产生一些可能因代理 - 任务分配而异的成本。 It is required to perform all tasks by assigning exactly one agent to each task and exactly one task to each agent in such a way that the total cost of the assignment is minimized. 需要通过为每个任务准确分配一个代理并为每个代理分配一个任务来执行所有任务,从而最大限度地降低分配的总成本。

The Assignment problem can be solved in polynomial time using The Hungarian algorithm . 使用匈牙利算法可以在多项式时间内解决赋值问题。 Variations on the problem involve more tasks than agents , which you can apply to your special cases where the sizes of the lists differ. 问题的变化涉及比代理更多的任务 ,您可以将这些任务应用于列表大小不同的特殊情况。

If you want a "quick 'n dirty" solution that should give decent results, consider adapting your current algorithm to be a probabilistic one. 如果你想要一个应该给出不错的结果的“快速'脏”解决方案, 可以考虑将你当前的算法调整为概率算法。 Weight each nearby red point according to how far away it is from the black point, and pick one at random by their weights. 根据距离黑点的距离对每个附近的红点进行加权,然后根据它们的权重随机选择一个红点。 You may want to square (or even cube) the distances, to discourage picking farther away points. 你可能想要对距离进行平方(甚至是立方体),以阻止选择更远的点。 Overall it should pick many of the same points as your original algorithm, but with a few differences here and there. 总的来说,它应该选择许多与原始算法相同的点,但这里和那里有一些差异。 Repeat as many times as feasible, and pick the best result. 尽可能多地重复,并选择最佳结果。

If you want something a bit less hand-wavey, consider modeling the problem as an asymmetric traveling salesman problem. 如果你想要一些不那么浪漫的东西,可以考虑将问题建模为不对称的旅行商问题。 Connect each black point to each red point, with a directional edge of weight proportional to its euclidean distance between them. 将每个黑点连接到每个红点,其重量的方向边缘与它们之间的欧氏距离成比例。 Then connect each red point to each black point, with a directional edge of weight 0. Then solve with an existing asymmetric TSP solver, and then add extra nodes + connect as normal if necessary. 然后将每个红点连接到每个黑点,使用权重为0的方向性边缘。然后使用现有的非对称TSP求解器求解,然后根据需要添加额外的节点+连接。 Note however, this will throw away of lot of useful information (for instance, that we don't particularly care which black node we connect with next), in exchange for being able to use existing software with tried and proven heuristics and optimizations. 但请注意,这将丢弃许多有用的信息(例如,我们并不特别关心我们下一步连接哪个黑色节点),以换取能够使用现有软件和经过验证的启发式和优化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM