簡體   English   中英

從3d空間中的一組點移動到具有最短可能累積距離的另一組點

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

我們有2個列表(黑色和紅色),每個列表包含3d空間中的多個點。 我們必須將每個黑點移動到一個紅點,並以這樣的方式進行,即移動的總距離是最小的。 列表可以具有不同的大小。

在2D空間中簡單正確的溶解: 正確的例子

解決方案不正確: 解決方案不正確


如果列表的大小不同,那么我們必須將點堆疊在彼此之上或將一個點分成多個點。

拆分示例: 分裂的例子

堆疊示例: 堆疊示例


我們對此問題的最佳嘗試遵循以下一般步驟:

  1. 如果紅點多於黑點,請選擇距離所有紅點最遠的黑點,並將其與最接近其位置且尚未匹配的紅點匹配。

  2. 重復步驟1,直到匹配所有黑點。

  3. 迭代剩余的紅點並將每個紅點與各自最近的黑點相匹配,從而將它們堆疊起來。 結果將如下所示: 在此輸入圖像描述

  4. 注意:如果黑點多於紅點,則第一步將查找最遠的紅點並將其與最近的黑點匹配,並對交換的顏色進行相同的處理。

一些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);
        }
}

此方法適用於像立方體這樣的簡單形狀。 在此輸入圖像描述

然而,當立方體位置在3d空間中從一組點到另一個點重疊時,它開始起作用。

在此輸入圖像描述

它甚至可以用更復雜的點來制作更有趣的東西: 在此輸入圖像描述

我不確定為什么會出現故障,我無法想出這個方法出錯的簡單2D示例。

我們在3天的時間里嘗試了3種不同的方法,似乎無法找到解決這個看似簡單問題的方法。

您可以將此解釋為分配問題 ,其中黑點是“代理”,紅點是“任務”(反之亦然),它們之間的距離是成本。

問題實例有許多代理和許多任務。 可以分配任何代理來執行任何任務,從而產生一些可能因代理 - 任務分配而異的成本。 需要通過為每個任務准確分配一個代理並為每個代理分配一個任務來執行所有任務,從而最大限度地降低分配的總成本。

使用匈牙利算法可以在多項式時間內解決賦值問題。 問題的變化涉及比代理更多的任務 ,您可以將這些任務應用於列表大小不同的特殊情況。

如果你想要一個應該給出不錯的結果的“快速'臟”解決方案, 可以考慮將你當前的算法調整為概率算法。 根據距離黑點的距離對每個附近的紅點進行加權,然后根據它們的權重隨機選擇一個紅點。 你可能想要對距離進行平方(甚至是立方體),以阻止選擇更遠的點。 總的來說,它應該選擇許多與原始算法相同的點,但這里和那里有一些差異。 盡可能多地重復,並選擇最佳結果。

如果你想要一些不那么浪漫的東西,可以考慮將問題建模為不對稱的旅行商問題。 將每個黑點連接到每個紅點,其重量的方向邊緣與它們之間的歐氏距離成比例。 然后將每個紅點連接到每個黑點,使用權重為0的方向性邊緣。然后使用現有的非對稱TSP求解器求解,然后根據需要添加額外的節點+連接。 但請注意,這將丟棄許多有用的信息(例如,我們並不特別關心我們下一步連接哪個黑色節點),以換取能夠使用現有軟件和經過驗證的啟發式和優化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM