使用std :: less创建一个std :: map绕过Origin

时间:2014-07-05 00:41:11

标签: c++ c++11 stdmap cartesian-coordinates

简介

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

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

我想象那个细胞:

4 3 2
5 0 1
6 7 8 9

会变成

0,1,2,3,....

问题

我应该如何围绕写一个std :: less,以便我可以包围我的点,就像这样?我怎样才能理解解决方案如何遵循strict weak ordering并避免其他陷阱? 最后,您将如何使用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
        }
    }
}

这是我的第一个问题,我试图遵循这些规则。如果我做错了什么,请支持我,我会尽力把事情整理好。感谢您的支持!

3 个答案:

答案 0 :(得分:2)

让我们尝试解决这个等效问题:写一个函数f : (Z, Z) -> Z,其中N是整数集,这样如果你开始从0开始从原点开始写数字,并以逆时针方向向外旋转,(x,y)处的数字为f(x,y)

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

  1. k - 螺旋线的最后一个元素,(k, -k)满足f(k, -k) = (2k+1)^2 - 1
  2. 每一个&#34;手臂&#34; k - 行(对于k> 0)有k+1个元素。
  3. (x,y)位于max(|x|, |y|) - thng。
  4. 使用上述内容,您可以根据哪个坐标定义梯级,为f提供分段描述。因此,您有一个恒定的时间方法来计算f。从功能上讲,您可以定义less( (x1,y1), (x2,y2) ) = less(f(x1,y1), f(x2,y2))

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

我为radius()定义angle()Point成员函数。 &#34;半径&#34;正在使用maximum norm max(abs(x), abs(y))为方形环提供与原点相等的距离。对于极角,我使用[0, 2 pi]中的角度约定。标准库数学函数atan2给出了[-pi, +pi]范围内的结果,因此我为负结果添加了2 pi

不是在std::less内专门化namespace std,而是为operator<定义自己的Point更容易,因为这会自动与std::less一起使用。

为了实现比较,我使用另一个C ++ 11工具,即forward_as_tuple,它采用Point结构并将其转换为std::tuple<int, int>。因为std::tuple已经有operator<做了正确的事情(按照你提供的顺序对其成员进行了词典比较),Point之间的比较现在使用半径优先和角度秒。

代码如下(并以正确的顺序打印点)。

#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";
}

Live Example打印

  

0,1,2,3,4,5,6,7,8,

注意:如果你将它扩展到第三个环,9将不会与8相邻,而是会得到一个稍微不同的螺旋

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

原因是角度为0将是新环的第一个元素。你可以调整一下,但angle()的代码会很快变得非常混乱(依赖于环)。另一方面,从原点到右边的数字是这样的奇数正方形(1,9,25等)。