您好!我正在编写一个在非平凡空间中运行的模拟。该系统占据了居中原点周围不确定的空间量。现在,我正在实现一个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
}
}
}
这是我的第一个问题,我试图遵循这些规则。如果我做错了什么,请支持我,我会尽力把事情整理好。感谢您的支持!
答案 0 :(得分:2)
让我们尝试解决这个等效问题:写一个函数f : (Z, Z) -> Z
,其中N
是整数集,这样如果你开始从0
开始从原点开始写数字,并以逆时针方向向外旋转,(x,y)
处的数字为f(x,y)
。
我们将使用以下观察结果:
k
- 螺旋线的最后一个元素,(k, -k)
满足f(k, -k) = (2k+1)^2 - 1
。k
- 行(对于k> 0)有k+1
个元素。(x,y)
位于max(|x|, |y|)
- thng。使用上述内容,您可以根据哪个坐标定义梯级,为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";
}
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等)。