C++ 使用std::less创建围绕原点缠绕的std::map 介绍

C++ 使用std::less创建围绕原点缠绕的std::map 介绍,c++,c++11,stdmap,cartesian-coordinates,C++,C++11,Stdmap,Cartesian Coordinates,你好!!我正在写一个在非平凡空间中运行的模拟。该系统在中心原点周围占据不确定的空间量。现在,我正在实现一个xy点类“Pos”,以连接坐标并充当容器(包含有限的数据块)的键。我希望原点周围的数据在内存中具有空间一致性 我这个问题的目标是为std::less编写一个专门化,如果(积分)位置被插入到地图中,它们将按照逆时针的顺序排列 我想细胞: 4.3.2 5 0 1 6789 将成为 0,1,2,3 问题: 我应该如何写一个std::less,这样我就可以像这样把我的观点概括起来?我如何理解解决方案

你好!!我正在写一个在非平凡空间中运行的模拟。该系统在中心原点周围占据不确定的空间量。现在,我正在实现一个xy点类“Pos”,以连接坐标并充当容器(包含有限的数据块)的键。我希望原点周围的数据在内存中具有空间一致性

我这个问题的目标是为std::less编写一个专门化,如果(积分)位置被插入到地图中,它们将按照逆时针的顺序排列

我想细胞:

4.3.2
5 0 1
6789

将成为

0,1,2,3

问题: 我应该如何写一个std::less,这样我就可以像这样把我的观点概括起来?我如何理解解决方案是如何遵循并避免其他陷阱的?
最后,使用C++11中可用的工具如何最好地处理或编写此函数

(如果使用无序贴图并通过动态原点周围的边界框线性迭代是一种更灵活、更有效的解决方案,请随意为其编写实现,但我不会将其标记为最佳答案。)


在一边 我一直在通过实施幼稚的尝试来学习,但我相信,通过讨论和合理的解释来解决这个问题比运气好

这里是一个背景快照

struct Pos
{
    short x;
    short y;
    Pos(short x, short y);
    Pos(const Pos& p);
    void operator=(const Pos& p);
    ~Pos() = default;
};
namespace std {
    template<> struct less<Pos> {
        bool operator()(const Pos& p1, const Pos& p2) const {
            //Implementation
        }
    }
}
struct Pos
{
短x;
短y;
位置(短x,短y);
Pos(const Pos&p);
无效运算符=(const Pos&p);
~Pos()=默认值;
};
名称空间标准{
无模板结构{
布尔运算符()(常量位置和p1、常量位置和p2)常量{
//实施
}
}
}

这是我的第一个问题,我试着遵守规则。如果我做错了什么,请支持我,我会尽力把事情安排好。谢谢你的支持

让我们试着解决这个等价的问题:写一个函数
f:(Z,Z)->Z
,其中
N
是一组整数,如果你从
0
开始写数字,从原点开始,以逆时针向外的螺旋线,
(x,y)
处的数字将是
f(x,y)

我们将使用以下观察结果:

  • 螺旋的第四级
    (k,-k)
    的最后一个元素满足
    f(k,-k)=(2k+1)^2-1
  • k
    第个横档(对于k>0)的每个“臂”都有
    k+1
    元素
  • (x,y)
    位于
    最大(|x |,|y |)
    第四个横档上

  • 使用上述内容,您可以根据坐标定义横档,为
    f
    提供分段描述。因此,您有一个常数时间方法来计算
    f
    。在功能上,您可以定义
    less((x1,y1),(x2,y2))=less(f(x1,y1),f(x2,y2))

    常用的解决方案是转换为极坐标


    您可以定义一个较小的运算符,先按到中心的距离排序,然后按角度排序。

    下面是一个使用C++11功能的完整且有效的解决方案

    我为
    点定义
    半径()
    角度()
    成员函数。“半径”是指使用
    max(abs(x)、abs(y))
    使方环与原点的距离相等。对于极角,我使用
    [0,2 pi]
    中的角度约定。标准库数学函数
    atan2
    给出了
    [-pi,+pi]
    范围内的结果,因此我为负结果添加了
    2pi


    与其在
    名称空间std
    中专门化
    std::less
    ,不如更容易定义自己的
    运算符。您需要一个函数,该函数接受X,Y对,并将其转换为表示其在螺旋上位置的序号。如果您的序列有间隙,但不能有重叠,这是可以的。所以我首先要找到你的点所在的“环”(abs(x-center)和abs(y-center)中较大的一个),然后以你的基值为该环内每个环的面积(类似于(ring-1)^2),然后依次添加你在当前环上的距离(找到你在哪条边上以及你在这条边上的距离)。当你的点按这个顺序排序时,它们将按螺旋顺序排列。我喜欢它!我的第一个想法涉及到距离的平方,然后是象限——但我在象限内的反射中遇到了碰撞。这肯定没有代码的简单性重要。我喜欢你的回答。这是一个简单、有效的答案,有具体的解释和一个可以在浏览器上编译和运行的c++11实现!我真的很感激。很高兴能帮上忙!你的半径公式是错误的。应该是sqrt(x^2+y^2)。@sammy no,一般来说,有p-范数定义为
    norm\u p(x)=(sum\u i(x\u i^p))^(1/p)
    。对于p=2,得到的是你提到的传统半径(毕达哥拉斯法则),对于p=1,得到的是曼哈顿“出租车”标准,对于p=infinity,得到的是一个方环作为半径。参见,例如,规范不足以为R^2空间中的所有元素提供严格的排序
    #include <cmath>        // abs, atan, atan2, max
    #include <iostream>     // cout
    #include <map>          // map
    #include <tuple>        // forward_as_tuple
    
    struct Point
    {
        int x, y;    
    
        int radius() const
        {
            // block-wise radius, use Pythagoras for ring-wise radius
            return std::max(std::abs(x), std::abs(y));
        }
    
        double angle() const
        {
            // result of atan2 in [-pi, +pi]
            auto a = std::atan2(y, x);
    
            // we want result in [0, 2 pi]
            if (a < 0)             
                a += 8 * std::atan(1); // add 2 pi
            return a;
        }
    
        friend bool operator<(Point const& L, Point const& R)
        {
            // delegate to operator< of std::tuple
            return 
                std::forward_as_tuple(L.radius(), L.angle()) < 
                std::forward_as_tuple(R.radius(), R.angle())
            ;
        }
    
        friend std::ostream& operator<<(std::ostream& os, Point const& p)
        {
            return os << "{" << p.x << ", " << p.y << "}"; 
        }
    };
    
    int main() 
    {    
        auto point_map = std::map<Point, int> { 
            {{-1, 1}, 4}, {{ 0, 1}, 3}, {{ 1, 1}, 2}, 
            {{-1, 0}, 5}, {{ 0, 0}, 0}, {{ 1, 0}, 1},
            {{-1,-1}, 6}, {{ 0,-1}, 7}, {{ 1,-1}, 8}
        };
    
        for (auto&& elem : point_map)
            std::cout << elem.second << ",";
        std::cout << "\n";
    }
    
    15 14 13 12 11
    16  4  3  2 10
    17  5  0  1  9
    18  6  7  8 24
    19 20 21 22 23