使用C ++(和Qt),我需要处理大量的3D坐标。
具体来说,当我收到一个3D坐标(由3个双打组成)时,我需要检查列表是否已经处理了这个坐标。 如果没有,那么我处理它并将其添加到列表(或容器)。
坐标数量可能变得非常大,因此我需要将处理过的坐标存储在一个容器中,以确保检查容器中是否已经包含3D坐标的速度很快。
我正在考虑使用地图地图的地图,存储x坐标,然后是y坐标,然后存储z坐标,但这使得使用起来非常繁琐,所以我实际上希望有很多更好的办法,我无法想到。
答案 0 :(得分:7)
加速此类处理的最简单方法可能是将已处理的点存储在Octree中。检查重复将接近对数。
另外,请确保通过检查点之间的距离而不是坐标的相等性来容忍舍入误差。
答案 1 :(得分:4)
您可以轻松使用以下内容:
#include <set>
#include <cassert>
const double epsilon(1e-8);
class Coordinate {
public:
Coordinate(double x, double y, double z) :
x_(x), y_(y), z_(z) {}
private:
double x_;
double y_;
double z_;
friend bool operator<(const Coordinate& cl, const Coordinate& cr);
};
bool operator<(const Coordinate& cl, const Coordinate& cr) {
if (cl.x_ < cr.x_ - epsilon) return true;
if (cl.x_ > cr.x_ + epsilon) return false;
if (cl.y_ < cr.y_ - epsilon) return true;
if (cl.y_ > cr.y_ + epsilon) return false;
if (cl.z_ < cr.z_ - epsilon) return true;
return false;
}
typedef std::set<Coordinate> Coordinates;
// Not thread safe!
// Return true if real processing is done
bool Process(const Coordinate& coordinate) {
static Coordinates usedCoordinates;
// Already processed?
if (usedCoordinates.find(coordinate) != usedCoordinates.end()) {
return false;
}
usedCoordinates.insert(coordinate);
// Here goes your processing code
return true;
}
// Test it
int main() {
assert(Process(Coordinate(1, 2, 3)));
assert(Process(Coordinate(1, 3, 3)));
assert(!Process(Coordinate(1, 3, 3)));
assert(!Process(Coordinate(1+epsilon/2, 2, 3)));
}
答案 2 :(得分:3)
将您的空间划分为不连续的垃圾箱。可以是无限深的正方形,也可以是立方体。将处理过的坐标存储在一个简单的链接列表中,如果您喜欢在每个bin中进行排序。获得新坐标后,跳转到封闭的bin,然后遍历列表以查找新点。
警惕浮点比较。您需要将值转换为整数(例如乘以1000并截断),或者确定将2个值视为相等的接近值。
答案 3 :(得分:2)
假设您已经有一个Coordinate类,添加一个哈希函数并维护一个坐标的hash_set。 看起来像是:
struct coord_eq
{
bool operator()(const Coordinate &s1, const Coordinate &s2) const
{
return s1 == s2;
// or: return s1.x() == s2.x() && s1.y() == s2.y() && s1.z() == s2.z();
}
};
struct coord_hash
{
size_t operator()(const Coordinate &s) const
{
union {double d, unsigned long ul} c[3];
c[0].d = s.x();
c[1].d = s.y();
c[2].d = s.z();
return static_cast<size_t> ((3 * c[0].ul) ^ (5 * c[1].ul) ^ (7 * c[2].ul));
}
};
std::hash_map<Coordinate, coord_hash, coord_eq> existing_coords;
答案 4 :(得分:1)
您是否期望/要求完全匹配?这些可能很难用双打来强制执行。例如,如果你已经处理(1.0,1.0,1.0)然后你收到(0.9999999999999,1.0,1.0)你会认为它是一样的吗?如果是这样,您将需要应用某种近似值或者定义误差范围。
然而,回答问题本身:首先想到的方法是创建一个索引(字符串或位串,取决于你想要的可读性)。例如,创建字符串“(1.0,1.0,1.0)”并将其用作地图的关键字。这样可以轻松查找地图,保持代码可读(并且还可以轻松地转储地图内容以进行调试)并为您提供合理的性能。如果你需要更快的性能,你可以使用散列算法以数字方式组合三个坐标,而无需通过字符串。
答案 5 :(得分:1)
如何使用boost :: tuple作为坐标,并将元组存储为地图的索引?
(你可能还需要从这个答案中做出除以ε的想法。)
答案 6 :(得分:1)
嗯,这取决于什么是最重要的...如果一个三重映射太繁琐而无法使用,那么实现其他数据结构是不值得的?
如果你想绕过tripple map解决方案的丑陋,只需将它包装在另一个带有三个参数的访问函数的容器类中,并在内部隐藏所有乱码。
如果您更担心这件事的运行时性能,将坐标存储在Octree中可能是个好主意。
另外值得一提的是,使用浮点数或双精度执行这些操作时,您应该非常小心精度 - 如果(0,0,0.01)与(0,0,0.01001)相同的坐标?如果是,您将需要查看您使用的比较函数,而不管数据结构如何。这也取决于我猜你的坐标来源。
答案 7 :(得分:1)
使用3D坐标的任何唯一变换并仅存储结果列表。
实施例: md5('X,Y,Z')是唯一的,您只能存储结果字符串。
哈希不是一个高效的想法,但你得到了这个概念。找到任何唯一的转换,你就拥有它。
/维伊
答案 8 :(得分:1)
使用std :: set。定义具有运算符&lt;的3d坐标(或使用boost :: tuple)的类型。定义。添加元素时,可以将其添加到集合中,如果已添加,则执行处理。如果没有添加(因为它已经存在),请不要进行处理。
但是,如果您使用的是双打,请注意您的算法可能会导致不可预测的行为。 IE,(1.0,1.0,1.0)与(1.0,1.0,1.00000000001)相同?
答案 9 :(得分:0)
好问题......它有很多解决方案,因为这类问题来了 在图形和科学应用程序中多次使用。
根据您需要的解决方案,它可能在引擎盖下相当复杂 少量代码并不一定意味着更快。
“但这使得使用”非常繁琐 - 一般来说,你可以绕过这个 typedef或wrapper类(强烈建议使用这种情况下的包装器)。
如果您不需要以任何空间重要方式使用3D坐标( 比如“给我P点X距离内的所有点”,然后我建议你 只是找到一种方法来散列每个点,并使用单个哈希映射... O(n)创建,O(1) 访问(检查它是否已被处理),你不能做得更好。
如果确实需要更多空间信息,则需要一个明确需要的容器 它考虑在内。 您选择的容器类型取决于您的数据集。如果你有好的 了解您收到的价值范围将有所帮助。
如果您在已知范围内收到分布均匀的数据...请使用octree。
如果您的分发版趋于群集,请使用k-d trees。你需要 在输入新的坐标后重建k-d树(不一定每次, 就在它变得过度失衡的时候)。简而言之,Kd树就像八叉树一样,但是没有统一的划分。
答案 10 :(得分:0)
为什么要这么麻烦?你正在做什么“处理”?除非它非常复杂,否则再次进行计算可能会更快,而不是浪费时间在巨大的地图或哈希表中查找。
这是关于现代cpu的更直观的事情之一。计算速度快,记忆力慢。
我意识到这不是你问题的真正答案,而是质疑你的问题。
答案 11 :(得分:0)
您可以使用std::set
3D坐标,也可以使用已排序的std::vector
。两者都会给你对数时间查找。在任何一种情况下,您都需要为3D坐标类实现小于比较运算符。
答案 12 :(得分:0)
有很多方法可以做到这一点,但你必须首先问问自己你的假设和条件是什么。
因此,假设您的空间大小有限并且您知道什么是最大准确度,那么您可以形成一个函数,给定(x,y,z)将它们转换为唯一的数字或字符串 - 这可以是只有当你知道你的准确性有限时才会完成(例如 - 没有两个实体可以占据相同的立方厘米)。 对坐标进行编码允许您使用带有O(1)的单个map / hash。
如果不是这种情况,您可以随时使用3个嵌入式地图,或者采用空间划分算法(如上所述的OcTree),尽管在平均搜索中给出了O(logN),它们也会给你您可能想要的其他信息(邻居,人口等),但当然更难实施。
答案 13 :(得分:0)
您可以轻松地为一级std::map
定义比较器,以便查找变得不那么麻烦。没有理由害怕这一点。比较器定义了映射的_Key模板参数的顺序。它也可以用于多图和集合集。
一个例子:
#include <map>
#include <cassert>
struct Point {
double x, y, z;
};
struct PointResult {
};
PointResult point_function( const Point& p ) { return PointResult(); }
// helper: binary function for comparison of two points
struct point_compare {
bool operator()( const Point& p1, const Point& p2 ) const {
return p1.x < p2.x
|| ( p1.x == p2.x && ( p1.y < p2.y
|| ( p1.y == p2.y && p1.z < p2.z )
)
);
}
};
typedef std::map<Point, PointResult, point_compare> pointmap;
int _tmain(int argc, _TCHAR* argv[])
{
pointmap pm;
Point p1 = { 0.0, 0.0, 0.0 };
Point p2 = { 0.1, 1.0, 1.0 };
pm[ p1 ] = point_function( p1 );
pm[ p2 ] = point_function( p2 );
assert( pm.find( p2 ) != pm.end() );
return 0;
}
答案 14 :(得分:0)
这方面的某些事情可能是:
struct Coor {
Coor(double x, double y, double z)
: X(x), Y(y), Z(z) {}
double X, Y, Z;
}
struct coords_thesame
{
bool operator()(const Coor& c1, const Coor& c2) const {
return c1.X == c2.X && c1.Y == c2.Y && c1.Z == c2.Z;
}
};
std::hash_map<Coor, bool, hash<Coor>, coords_thesame> m_SeenCoordinates;
未经测试,自担风险使用:)
答案 15 :(得分:0)
无论你的存储方法是什么,我建议你决定使用epsilon(区分两个坐标的最小浮点距离),然后将所有坐标除以epsilon,并将它们存储为整数。
答案 16 :(得分:0)
您可以使用任何可散列类型的hash_set - 例如,将每个元组转换为字符串“(x,y,z)”。 hash_set执行快速查找但很好地处理冲突。
答案 17 :(得分:0)
如果你编写一个带有简单公共接口的帮助器类,这大大减少了实现细节的实际繁琐,例如使用map<map<map<>>>
。封装之美!
那就是说,你可能能够装配一个hashmap来很好地完成这个技巧。只需将三个双打混合在一起,就可以得到整个点的关键。如果您担心具有对称坐标的点之间的许多碰撞(例如,(1,2,3)和(3,2,1)等),只需使哈希键相对于x,y不对称,和z坐标,使用位移等等。
答案 18 :(得分:0)
选择一个常数来缩放坐标,以便1个单位描述一个可接受的小方框,然而按大小分组的最大分量的整数部分将适合32位整数;将结果的X,Y和Z分量转换为整数并将它们一起散列。使用它作为映射或散列表的散列函数(不作为数组索引,您需要处理冲突)。
在比较坐标时,您可能还需要考虑使用软糖因子,因为您可能会得到略微不同的浮点值,并且通常最好将它们焊接在一起以避免渲染时出现裂缝。