我正在制作一个游戏,其中(x, y)
和x
y
位置ints
处可能只存在一个对象。例如,一个对象可能存在于(0, 0)
或者它可能不存在,但是不可能同时存在多个对象。
我正在尝试确定哪个STL容器用于解决此问题的最佳方法。
基本上,我从一个对象及其(x, y)
位置开始。目标是根据该对象的周围对象确定最高,最大可能的矩形。必须使用当前对象上方和下方的所有对象创建矩形。也就是说,它必须是最高的,它可能基于起始物体位置。
例如,假设以下代表我的对象网格,并且我从位置(3, 4)
处的绿色对象开始:
然后,我正在寻找的矩形将由下面的粉红色方块表示:
因此,假设我从(3, 4)
处的对象开始,如示例所示,我将需要检查对象是否也存在于(2, 4)
,(4, 4)
,(3, 3)
,和(3, 5)
。如果某个对象存在于任何这些位置,我需要重复该对象的过程以找到最大可能的矩形。
这些物品相当罕见,游戏世界庞大。仅仅new
整个游戏世界的2D数组似乎不切实际,因为大多数元素都是空的。但是,我需要索引到任何位置以检查对象是否随时存在。
相反,我考虑使用像这样的std::map
:
std::map< std::pair<int, int>, ObjectData> m_objects;
然后,当我检查周围的对象时,我可以在循环中使用map::find()
,检查周围的对象是否存在:
if(m_objects.find(std::pair<3, 4>) != m_objects.end())
{
//An object exists at (3, 4).
//Add it to the list of surrounding objects.
}
如果我决定这样做,我可能会对map::find()
进行大量调用,但是地图占用的内存比new
整个世界的2D数组少得多。< / p>
有没有人对我可以用来找到我要找的东西的简单算法有任何建议?我应该继续使用std::map
还是有更好的容器来解决这样的问题?
答案 0 :(得分:1)
您需要在每个网格位置存储多少数据?如果您只是在寻找一个标志着邻居的旗帜,那么您至少有两个“低技术”解决方案
a)如果您的网格稀疏,每个方块如何保留邻居列表?所以每个方块都知道哪些相邻的方格被占用。当一个正方形被占用或腾空时,你需要做一些工作来维护列表。但邻居列表意味着您根本不需要网格地图
b)如果网格地图位置确实只是点,则每个网格位置使用1位。结果映射将比每个网格点使用字节的结果映射小8x8 = 64倍。位操作正在快速减轻。 10,000x10,000地图将占用100,000,000位或12.5MB(大约)
答案 1 :(得分:0)
如果可能,改进将是使用散列映射。这将允许您至少进行潜在的广泛搜索,预期时间复杂度为O(1)。
这里有一个线程(Mapping two integers to one, in a unique and deterministic way)详细介绍了如何将两个整数散列在一起。
如果您的编译器支持C ++ 11,则可以使用std :: unordered_map。如果没有,boost基本上是相同的:http://www.boost.org/doc/libs/1_38_0/doc/html/boost/unordered_map.html
答案 2 :(得分:0)
您可能需要考虑空间数据结构。如果数据“稀疏”,正如您所说,那么进行四叉树邻域搜索可能会为您节省大量处理能力。我个人会使用一个R树,但这很可能是因为我有一个我编写的R树库,可以轻松导入。
例如,假设您有一个包含10,000个元素的1000x1000
网格。假设目前,均匀随机分布,我们(基于密度)预期不会超过,比方说。 。 。在任一维度上接触的三到五个物体的链(在这个密度下,三个垂直取向的物体的链将以0.01%的时间概率发生)。假设所考虑的对象位于(x,y)
。从(x-5,y-5)
开始并前往(x+5,y+5)
的窗口搜索将为您提供最多121个元素的列表,以执行线性搜索。如果你的矩形拣选算法注意到可以形成一个更高的矩形(即如果正在考虑的矩形触及这个11x11
边界框的边缘),只需重复窗口搜索另一个5x5
} {}区域在原始的一个方向。根据需要重复。
这当然只有在数据非常稀疏的情况下才能正常工作。可能值得调整R树,使得叶子是一个关联。数据结构(即Int -> Int -> Object
),但在那时,最好找到适用于更密集数据的解决方案。
我可能过度思考了这一点;在某个地方可能会有一个更简单的解决方案。
关于R树的一些参考文献:
如果我开始对它进行清理,我将使用指向我自己的R-tree实现(公共域)的链接进行编辑。
答案 3 :(得分:0)
这听起来像一个家庭作业问题(因为它有这种奇怪的条件&#34;矩形必须通过使用当前对象上方和下方的所有对象来创建#34;这使得解决方案变得微不足道)。但无论如何我还是试一试。我将使用&#34;像素&#34;而不是&#34;对象&#34;,为方便起见。
如果您的应用程序确实值得重量级解决方案,您可以尝试将像素存储在四叉树中(其叶子包含每个只有几千个像素的普通旧2D阵列)。或者您可以将连续的像素组合成&#34;形状&#34; (例如,你的例子只包含一个&#34;形状&#34;,即使它包含24个单独的像素)。给定一个初始的非结构化像素坐标列表,很容易找到这些形状; google&#34; union-find&#34;。存储连续形状的具体好处是,当您查找最大的矩形时,您只需要考虑与初始像素形状相同的像素。
存储连续形状的一个特定缺点是,如果你的像素对象在移动(例如,如果它们代表roguelike游戏中的怪物),我不确定union-find数据结构是否支持增量更新。您可能必须在每个&#34;框架#34;上运行union-find,这将非常糟糕。
无论如何......让我们说你正在使用std::unordered_map<std::pair<int,int>, ObjectData*>
,因为这对我来说听起来很合理。 (你几乎肯定会在地图中存储指针,而不是实际的对象,因为复制所有这些对象比复制指针要快得多。)
typedef std::pair<int, int> Pt;
typedef std::pair<Pt, Pt> Rectangle;
std::unordered_map<Pt, ObjectData *> myObjects;
/* This helper function checks a whole vertical stripe of pixels. */
static bool all_pixels_exist(int x, int min_y, int max_y)
{
assert(min_y <= max_y);
for (int y = min_y; y <= max_y; ++y) {
if (myObjects.find(Pt(x, y)) == myObjects.end())
return false;
}
return true;
}
Rectangle find_tallest_rectangle(int x, int y)
{
assert(myObjects.find(Pt(x,y)) != myObjects.end());
int top = y;
int bottom = y;
while (myObjects.find(Pt(x, top-1) != myObjects.end()) --top;
while (myObjects.find(Pt(x, bottom+1) != myObjects.end()) ++bottom;
// We've now identified the first vertical stripe of pixels.
// The next step is to "paint-roller" that stripe to the left as far as possible...
int left = x;
while (all_pixels_exist(left-1, top, bottom)) --left;
// ...and to the right.
int right = x;
while (all_pixels_exist(right+1, top, bottom)) ++right;
return Rectangle(Pt(top, left), Pt(bottom, right));
}