Algorithm 圆叠加算法

Algorithm 圆叠加算法,algorithm,graph-algorithm,Algorithm,Graph Algorithm,在我制作的游戏中,我需要堆叠一堆不同半径的圆,这样就不会有堆叠重叠。将这些圆堆叠起来,以便重叠的圆与顶部半径最大的圆形成一个堆栈。圆随机放置在连续的二维平面上。可以有半径相等的圆 < >我使用C++。这些圆存储在向量中。关于平面,我只是指圆有(双)x和y坐标。堆栈本质上是使用最上面的一个(应该是最大的)的位置和半径的圆的向量。所谓“无堆叠重叠”,我的意思是堆叠圆后,堆叠不应重叠 我所做的: 按半径对圆排序 对于第一个圆(最大的一个),将所有重叠的圆添加到其堆栈中,并将其从圆列表中删除 重复此操作

在我制作的游戏中,我需要堆叠一堆不同半径的圆,这样就不会有堆叠重叠。将这些圆堆叠起来,以便重叠的圆与顶部半径最大的圆形成一个堆栈。圆随机放置在连续的二维平面上。可以有半径相等的圆

< >我使用C++。这些圆存储在向量中。关于平面,我只是指圆有(双)x和y坐标。堆栈本质上是使用最上面的一个(应该是最大的)的位置和半径的圆的向量。所谓“无堆叠重叠”,我的意思是堆叠圆后,堆叠不应重叠

我所做的:

  • 按半径对圆排序
  • 对于第一个圆(最大的一个),将所有重叠的圆添加到其堆栈中,并将其从圆列表中删除
  • 重复此操作,直到圆圈列表为空
  • 对所有堆栈进行排序,以获得顶部最大的堆栈
  • 为什么它不起作用(总是)。有时3个半径相等的圆重叠,因此两个圆共享一个圆(但它们本身不重叠)。其中一个圆获得共享圆,然后碰巧该圆被选为顶部圆,从而导致两个重叠堆栈

    我已经考虑了很多,但我能想到的所有方法似乎都需要非常复杂的循环和ifs来手动检查共享哪些圆以及如何移动它们,或者通过随机重新排列堆栈来使用蛮力和运气

    是否有一些处理此类问题的一般方法?如果堆栈是“最优”的,也就是说,所有堆栈都最小化了它们的“能量”(就像它们拉入节点的距离最小化一样),这也会很酷

    在情况1中,选择了中心圆(假设所有三个大圆的半径相同),因为它最小化了堆叠的数量。在情况2中,最大的圆正确地位于堆栈顶部。在情况3中,一切都按预期进行。在情况4中,小圆圈错误地到达堆栈顶部。另外,如果其他两个圆的大小相等,则应只有一个堆栈,如情况1所示。在情况5中,错误的圆圈位于顶部,导致重叠堆栈

    谢谢

    工作暴力版本:

        std::vector<std::vector<Symbol>::iterator >symPos; std::vector<int> cons;std::vector<Symbol> selVec; Symbol start; SymbolStack stack;
        while(symbols.size()){
            std::sort(symbols.begin(),symbols.end(),std::greater<Symbol>());
            start = symbols.front();
            for(int i = 0;i<symbols.size();i++){
                if(symbols[i].getRadius() == start.getRadius()){
                    selVec.push_back(symbols[i]); cons.push_back(0); symPos.push_back(symbols.begin()+i);
                }
            }
            for(int i = 0;i<selVec.size();i++){
                for(int j = i+1;j<selVec.size();j++){
                    if((selVec[i].getPos()-selVec[j].getPos()).len() < 2*(selVec[i].getRadius()+selVec[j].getRadius())){
                        cons[i]++;cons[j]++;
                    }
                }
            }
            int maxCons = 0; int selected;
            for(int i = 0;i<cons.size();i++){
                if(cons[i] >= maxCons){selected = i;maxCons = cons[i];}
            }
            start = selVec[selected];
            stack.addSymbol(start); symbols.erase(symPos[selected]);
            for(auto it = symbols.begin();it!=symbols.end();){
                if((it->getPos()-start.getPos()).len() < 1.5*(it->getRadius()+start.getRadius())){
                    stack.addSymbol(*it);
                    it = symbols.erase(it);
                }else{
                    it++;
                }
            }
            stacks.push_back(stack);
            stack.clear();
            selVec.clear();
            symPos.clear();
            cons.clear();
        }
    
    std::vectorsymos;std::向量cons;std::vector selVec;符号启动;符号堆叠;
    while(symbols.size()){
    std::sort(symbols.begin()、symbols.end()、std::greater());
    start=symbols.front();
    
    对于(int i=0;i我将算法分为两部分-代表性检测和堆栈构建,其中堆栈构建基本上将每个圆与代表关联,形成堆栈

    最后一部分很简单。迭代每个圆并将其与能量最小的代表关联(可能是最接近的一个)。使用加速数据结构(如网格或kd树)来增强此类查询

    第一部分要难得多。事实上,它看起来是NP难的,虽然我不能证明它。但是让我们从一开始

    按大小降序排列圆是一个好主意。如果第一个圆(最大半径)没有与相同半径的圆重叠,它显然应该具有代表性。在这种情况下,请从列表中删除(或标记)每个重叠圆。在另一种情况下,您必须决定重叠圆中的哪一个(半径相等的)选择

    在您的代码中,您使用一个简单的启发式(重叠圆的数量)来决定选择哪个圆。根据您的数据和用例,这可能是一个有效的选项。一般来说,这可能并不总是导致最佳解决方案(因为决策可能会显著改变后续决策)

    另一个选择是使用回溯。尝试一个决定,看看结果如何(最后评估代表人数)。然后,在返回时,还可以尝试其他决策。当代表人数超过迄今为止看到的最小人数时,您可能会离开决策分支。不幸的是,在最坏的情况下,这可能会导致指数级运行时间。但您只需在大小相同的圆圈重叠的情况下进行决策。如果不出现这种情况,则r通常,回溯可能是一个很好的选择,它可以保证您获得全局最优的解决方案


    请记住,您的列表是经过排序的。当您搜索特定半径的圆时,不必搜索整个列表。代码中有几个地方可以通过这一事实加以改进。并且,如前所述,使用加速结构可以更快地评估重叠查询。

    我将算法分为两部分-repres动态检测和堆栈构建,其中堆栈构建基本上将每个圆与代表关联,形成堆栈

    最后一部分很简单。迭代每个圆并将其与能量最小的代表关联(可能是最接近的一个)。使用加速数据结构(如网格或kd树)来增强此类查询

    第一部分要难得多。事实上,它看起来是NP难的,虽然我不能证明它。但是让我们从一开始

    按大小降序排列圆是一个好主意。如果第一个圆(最大半径)没有与相同半径的圆重叠,它显然应该具有代表性。在这种情况下,请从列表中删除(或标记)每个重叠圆。在另一种情况下,您必须决定重叠圆中的哪一个(半径相等的)选择

    在您的代码中,您使用一个简单的启发式(重叠圆的数量)来决定选择哪个圆。根据您的数据和用例,这可能是一个有效的选项。一般来说,这可能并不总是导致最佳解决方案(因为决策可能会显著改变后续决策)

    另一个选择是使用回溯跟踪。尝试一个d