优化象限选择

时间:2010-07-22 21:58:06

标签: c# optimization bit-manipulation

我正在研究一个将项目细分为象限的数据结构,我发现的一个瓶颈是我选择点的象限的方法。不可否认,它相当简单,但它被召唤了很多次,它加起来。我想有一种有效的方法可以把它变成我想要的东西,但我想不出来。

private int Quadrant(Point p)
{
    if (p.X >= Center.X)
        return p.Y >= Center.Y ? 0 : 3;
    return p.Y >= Center.Y ? 1 : 2;
}

Center的类型为Point,坐标为ints。是的,我运行了代码配置文件,不,这不是过早的优化。


因为这只是在内部使用,我认为我的象限 不在Cartesian order,只要它们的范围是0-3。

4 个答案:

答案 0 :(得分:3)

C / C ++中最快的方式是

(((unsigned int)x >> 30) & 2) | ((unsigned int)y >> 31)

(30/31或62/63,取决于int的大小)。 这将使象限为0,2,3,1。

编辑LBushkin:

(((unsigned int)(x - center.x) >> 30) & 2) | ((unsigned int)(y-center.y) >> 31)

答案 1 :(得分:1)

我不知道你可以在C#中大大加快这段代码的速度。但是,你可以做什么,看看你是如何处理点的,看看你是否可以避免对这种方法进行不必要的调用。也许你可以创建一个QuadPoint结构来存储一个点所在的象限(在你计算一次之后),这样你就不必再这样做了。

但是,诚然,这取决于您的算法正在做什么,以及是否可以存储/记忆象限信息。如果每一点都是完全独特的,这显然无济于事。

答案 2 :(得分:1)

我刚刚被告知正确排序产生0,1,2,3象限结果的解决方案:

#define LONG_LONG_SIGN (sizeof(long long) * 8 - 1)

double dx = point.x - center.x;
double dy = point.y - center.y;

long long *pdx = (void *)&dx;
long long *pdy = (void *)&dy;

int quadrant = ((*pdy >> LONG_LONG_SIGN) & 3) ^ ((*pdx >> LONG_LONG_SIGN) & 1);

此解决方案适用于double类型的x,y坐标。

我已经对这个方法进行了一些性能测试,并且在原始问题中进行了分支方法:我的结果是分支方法总是快一点(目前我有稳定的160/180关系)< / strong>,所以我更喜欢使用按位运算的方法的分支方法。


<强>更新

如果有人感兴趣,所有三种算法都合并为EKAlgorithms C/Objective-C repository作为“笛卡尔象限选择”算法:

  1. 原始分支算法
  2. @ruslik从接受的答案中逐位算法。
  3. 我的一位同事按比例推荐,比第二种算法慢一点,但按正确的顺序返回象限。
  4. 所有算法都经过优化,可以处理双类型点。

    性能测试向我们展示了一般来说,Mac OS X上的第一个分支算法是胜利者,但在Linux机器上我们确实看到第二个算法的执行速度比分支算法快一点。

    因此,一般的结论是坚持使用分支算法,因为按位版本不会带来任何性能提升。

答案 3 :(得分:0)

我的第一次尝试是摆脱嵌套的条件。

int xi = p.X >= Center.X ? 1 : 0;
int yi = p.Y >= Center.Y ? 2 : 0;
int quadrants[4] = { ... };
return quadrants[xi+yi];

如果象限被允许重新编号,则象限中的数组查找是可选的。我的代码仍需要两次比较,但它们可以并行完成。

我提前为任何C#错误道歉,因为我通常编写C ++代码。


当两个无符号31位坐标存储在64位无符号长变量中时,也许更有效率。

// The following two lines are unnecessary
// if you store your coordinated in unsigned longs right away
unsigned long Pxy = (((unsigned long)P.x) << 32) + P.y;
unsigned long Centerxy = (((unsigned long)Center.x) << 32) + Center.y;

// This is the actual calculation, only 1 subtraction is needed.
// The or-ing with ones hast only to be done once for a repeated use of Centerxy.
unsigned long diff = (Centerxy|(1<<63)|(1<<31))-Pxy;
int quadrant = ((diff >> 62)&2) | ((diff >> 31)&1);

退一步,可以采用不同的解决方案。不要将您的数据结构立即分成象限,而是在两个方向上交替排列。这也是在相关的Kd-tree

中完成的