我需要一个C语言的空间索引
我正在努力,希望摆脱现有的简单的基于瓷砖的系统1,转而使用真正的空间索引2 有效地查找点的算法是不够的:我需要查找范围非零的对象。考虑一下具有边界矩形的对象,它几乎可以捕获索引中所需的详细程度。给定一个搜索矩形,我需要能够高效地找到其边界矩形位于搜索矩形内部或与搜索矩形相交的所有对象 索引不能是只读的:gschem是一个原理图捕获程序,它的全部目的是围绕原理图移动内容。所以事情将会发生变化。所以,虽然我能负担得起插入比搜索稍微贵一点的费用,但也不能太贵,而且删除也必须是可能的,而且相当便宜。但最重要的要求是渐近性:如果搜索不能是O(1),则搜索应该是O(logn)。插入/删除最好是O(logn),但O(n)可以。我绝对不想要任何>O(n)(每个操作;显然,所有对象操作都需要O(n log n) 我有什么选择?我觉得自己不够聪明,无法评估。理想情况下,会有一些C库为我做所有聪明的事情,但我会机械地实现一个算法,如果必须的话,我可能完全理解,也可能不完全理解。顺便说一下,如果有助于推荐的话,gEDA会使用glib 脚注: 1标准gEDA将原理图划分为固定数量(目前为100)的“分幅”,用于加快在边框中搜索对象的速度。这显然足以使大多数示意图的搜索速度足够快,但这样做会导致其他问题:太多的函数需要指向事实上全局对象的指针。瓷砖的几何结构也是固定的:只需平移(并可能缩放)到只有一块瓷砖覆盖的区域,就有可能完全破坏这种瓷砖系统我需要一个C语言的空间索引,c,glib,C,Glib,我正在努力,希望摆脱现有的简单的基于瓷砖的系统1,转而使用真正的空间索引2 有效地查找点的算法是不够的:我需要查找范围非零的对象。考虑一下具有边界矩形的对象,它几乎可以捕获索引中所需的详细程度。给定一个搜索矩形,我需要能够高效地找到其边界矩形位于搜索矩形内部或与搜索矩形相交的所有对象 索引不能是只读的:gschem是一个原理图捕获程序,它的全部目的是围绕原理图移动内容。所以事情将会发生变化。所以,虽然我能负担得起插入比搜索稍微贵一点的费用,但也不能太贵,而且删除也必须是可能的,而且相当便宜。但最
一个合理的答案是保留瓷砖系统的元素,但修复其弱点:教它跨越整个空间,必要时再细分。但是,在我专制地决定这是最好的方法之前,我希望其他人加上他们的两分钱。这听起来像是一个非常适合四叉树的应用程序(假设你只对2D感兴趣)。四叉树是分层的(适合搜索),它的空间分辨率是动态的(在需要它的区域允许更高的分辨率)
我总是滚动我自己的四叉树,但这里有一个看起来合理的库:这很容易做到。很难做到快。听起来像是我研究的一个问题,其中有一个巨大的最小值和最大值列表,给定一个值,它必须返回有多少最小值和最大值对重叠该值。你只需要把它分成两个维度。所以每个方向有两棵树。然后在结果上做一个交集。这真的很快
#include <iostream>
#include <fstream>
#include <map>
using namespace std;
typedef unsigned int UInt;
class payLoad {
public:
UInt starts;
UInt finishes;
bool isStart;
bool isFinish;
payLoad ()
{
starts = 0;
finishes = 0;
isStart = false;
isFinish = false;
}
};
typedef map<UInt,payLoad> ExtentMap;
//==============================================================================
class Extents
{
ExtentMap myExtentMap;
public:
void ReadAndInsertExtents ( const char* fileName )
{
UInt start, finish;
ExtentMap::iterator EMStart;
ExtentMap::iterator EMFinish;
ifstream efile ( fileName);
cout << fileName << " filename" << endl;
while (!efile.eof()) {
efile >> start >> finish;
//cout << start << " start " << finish << " finish" << endl;
EMStart = myExtentMap.find(start);
if (EMStart==myExtentMap.end()) {
payLoad pay;
pay.isStart = true;
myExtentMap[start] = pay;
EMStart = myExtentMap.find(start);
}
EMFinish = myExtentMap.find(finish);
if (EMFinish==myExtentMap.end()) {
payLoad pay;
pay.isFinish = true;
myExtentMap[finish] = pay;
EMFinish = myExtentMap.find(finish);
}
EMStart->second.starts++;
EMFinish->second.finishes++;
EMStart->second.isStart = true;
EMFinish->second.isFinish = true;
// for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++)
// cout << "| key " << EMStart->first << " count " << EMStart->second.value << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish << endl;
}
efile.close();
UInt count = 0;
for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++)
{
count += EMStart->second.starts - EMStart->second.finishes;
EMStart->second.starts = count + EMStart->second.finishes;
}
// for (EMStart=myExtentMap.begin(); EMStart!=myExtentMap.end(); EMStart++)
// cout << "||| key " << EMStart->first << " count " << EMStart->second.starts << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish << endl;
}
void ReadAndCountNumbers ( const char* fileName )
{
UInt number, count;
ExtentMap::iterator EMStart;
ExtentMap::iterator EMTemp;
if (myExtentMap.empty()) return;
ifstream nfile ( fileName);
cout << fileName << " filename" << endl;
while (!nfile.eof())
{
count = 0;
nfile >> number;
//cout << number << " number ";
EMStart = myExtentMap.find(number);
EMTemp = myExtentMap.end();
if (EMStart==myExtentMap.end()) { // if we don't find the number then create one so we can find the nearest number.
payLoad pay;
myExtentMap[ number ] = pay;
EMStart = EMTemp = myExtentMap.find(number);
if ((EMStart!=myExtentMap.begin()) && (!EMStart->second.isStart))
{
EMStart--;
}
}
if (EMStart->first < number) {
while (!EMStart->second.isFinish) {
//cout << "stepped through looking for end - key" << EMStart->first << endl;
EMStart++;
}
if (EMStart->first >= number) {
count = EMStart->second.starts;
//cout << "found " << count << endl;
}
}
else if (EMStart->first==number) {
count = EMStart->second.starts;
}
cout << count << endl;
//cout << "| count " << count << " key " << EMStart->first << " S " << EMStart->second.isStart << " F " << EMStart->second.isFinish<< " V " << EMStart->second.value << endl;
if (EMTemp != myExtentMap.end())
{
myExtentMap.erase(EMTemp->first);
}
}
nfile.close();
}
};
//==============================================================================
int main (int argc, char* argv[]) {
Extents exts;
exts.ReadAndInsertExtents ( "..//..//extents.txt" );
exts.ReadAndCountNumbers ( "..//../numbers.txt" );
return 0;
}
数字文件如下所示:
102731
104279
109316
104859
102165
105762
101464
100755
101068
108442
107777
101193
104299
107080
100958
.....
即使从磁盘读取这两个文件,扩展数据块的大小也为1.5mb,数字为780k,还有大量的值和查找,这只需几分之一秒。如果在内存中,速度会非常快。您的需求听起来与游戏和物理模拟的碰撞检测算法非常相似。有几个开源的C++库,它们可以在2-D或3-D中处理。尽管您的问题是针对C语言的,但您可能会发现它们的文档和实现很有用 通常分为以下几个部分:
broadphase通常使用O(N logn)算法实现,如。您可以通过将它与当前的tile方法结合使用(描述了这种混合方法)来加速这一过程。窄相位对于每一对来说都是相当昂贵的,并且可能会超出您的需要。在这一步中,通常用于凸面对象,但对于更特殊的情况(例如:长方体/圆和长方体/球体碰撞),存在更快的算法。对于点和线的混合,一个好的数据结构应该是一个R-树或它的一个衍生物(例如R*-树或Hilbert R-树)。鉴于您希望此索引是动态的和可序列化的,我认为使用将是一种合理的方法
如果您可以容忍C++,它有一个成熟的、灵活的R树实现,它支持动态插入/删除和序列化。
uHM,我不需要索引是可串行化的,因为它需要从磁盘上写入并加载它。我不确定这是否就是你在这里的意思。啊,我想你应该把它以文件格式保存到磁盘以提高速度;也许这只是一种奖励。不,绝对不会保存派生信息。文件格式是可编辑的文本(如果有点巴洛克风格的话),所以我不想要求人工编辑器担心保存的空间索引。然后您可以忽略这些功能并将其用作“内存”数据库。事实证明,我可以安装libspatialindex1,这是一个额外的好处。它似乎也有C绑定。谢谢你的参考;我搜索包列表的时候太特别了(对于r-tree之类的东西)。我来试试。谢谢你给我添加了细微差别。就我的问题而言,第二个更慢的窄相位是不存在的。用AABB(谢谢你的行话)来近似一切都是很好的。在这个代码练习的中间阶段,我已经用蛮力的方式来做了,消除了瓦片并简单地通过li迭代102731
104279
109316
104859
102165
105762
101464
100755
101068
108442
107777
101193
104299
107080
100958
.....