非冗余对列表中的对象索引

时间:2012-11-28 07:20:40

标签: c++ math collision-detection

我正在实现一个碰撞检测算法,用于存储单个八叉树节点中所有对象之间的距离。例如,如果节点中有4个对象,则对象1和2,1和3,1和4,2和3,2和4以及3和4之间存在距离。总对数的公式是t = n *(n-1)/ 2,其中t是对的总数,n是节点中对象的数量。

我的问题是,如何从列表中的位置转换为一对对象。例如,使用上面的对列表,3将返回对2& 3。

为了节省内存空间,列表只是距离的浮点列表,而不是包含距离和指向2个对象的指针。

我不确定如何在数学上将单个列表索引转换为一对数字。任何帮助都会很棒。我希望能够将其分解为2个函数,第一个返回第一个对象,第二个返回第二个,两个函数都采用2个变量,一个是索引,另一个是整个对象。节点。如果可能的话,我想创建一个没有任何循环或具有递归函数的函数,因为这将为我的碰撞检测算法实时运行。

2 个答案:

答案 0 :(得分:4)

更好的订购

我建议使用colexicographical order,因为在这种情况下,您不必提供对象总数。像这样订购你的对:

 0:   1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  …
0&1, 0&2, 1&2, 0&3, 1&3, 2&3, 0&4, 1&4, 2&4, 3&4, 0&5, 1&5, 2&5, 3&5, …

您可以将此列表扩展为无限长度,因此您可以在不知道项目数的情况下知道任何一对的索引。这样做的好处是,当您向数据结构中添加新项时,您只需要附加到数组,而不是重新定位现有条目。我已经将索引调整为零,因为你标记了你的问题C ++所以我假设你将使用从零开始的索引。我在下面的所有答案都假设了这个顺序。

你也可以像这样想象colex排序:

 a: 0  1  2  3  4  5 …
b:
1   0
2   1  2     index of
3   3  4  5       a&b
4   6  7  8  9
5  10 11 12 13 14
6  15 16 17 18 19 20
⋮   ⋮                 ⋱

配对单个索引

让我们先把一对变成一个索引。诀窍在于,对于每一对,你看第二个位置并想象所有在该位置具有较少数量的对。因此,例如对于2&4对,您首先计算第二个数小于4的所有对。这是从一组4中选择两个项目的可能方式的数量(即数字0到3) ,所以你可以把它表达为二项式系数4C2。如果你评价它,你最终得到4(4-1)/ 2 = 6。为此添加第一个数字,因为这是具有较低索引但在第二个位置具有相同数字的对的数量。对于2&4,这是2,因此2&4的整体索引为4(4-1)/ 2 + 2 = 8。

一般来说,对于一对 a & b ,索引将是 b b -1 )/ 2 +的

int index_from_pair(int a, int b) {
  return b*(b - 1)/2 + a;
}

要配对的单个索引

将单个索引 i 变回一对数字的一种方法是增加 b 直到 b b < / i> +1)/ 2&gt; i ,即 b 的下一个值将导致索引大于 i 的情况。然后你可以找到 a 作为差异 a = i - b b -1)/ 2。这种方法一次增加一个 b 包括使用循环。

pair<int, int> pair_from_index(int i) {
  int a, b;
  for (b = 0; b*(b + 1)/2 <= i; ++b)
    /* empty loop body */;
  a = i - b*(b - 1)/2;
  return make_pair(a, b);
}

您还可以将 b b -1)/ 2 = i 解释为二次方程式,您可以使用正方形求解根。你需要的真正的 b 是浮点 b 的底限,你得到的是这个二次方程的正解。由于此方法中由于舍入错误可能会遇到问题,您可能需要检查 b b +1)/ 2&gt; I 的。如果不是这种情况,请像在循环方法中那样增加 b 。一旦你有了 b a 的计算就会保持不变。

pair<int, int> pair_from_index(int i) {
  int b = (int)floor((sqrt(8*i + 1) + 1)*0.5);
  if (b*(b + 1)/2 <= i) ++b; // handle possible rounding error
  int a = i - b*(b - 1)/2;
  return make_pair(a, b);
}

顺序访问

请注意,您只需将索引转回对即可随机访问列表。迭代所有对时,一组嵌套循环更容易。而不是

for (int = 0; i < n*(n - 1)/2; ++i) {
  pair<int, int> ab = pair_from_index(i);
  int a = ab.first, b = ab.second;
  // do stuff
}

你最好写

for (int i = 0, b = 1; b != n; ++b) {
  for (int a = 0; a != b; ++a) {
    // do stuff
    ++i;
  }
}

答案 1 :(得分:2)

根据我对这个问题的理解,从索引(从0开始,在你的例子中为3)和对象数量中获得一对&amp; b(在你的例子中为1,2和3)的一种方法n(在你的例子中为4)是:

t = n * (n - 1) / 2;
a = n - floor((1 + sqrt(1 + 8 * (t - index - 1))) / 2);
b = index + (n - a) * (n - a + 1) / 2 - t + a + 1;

http://oeis.org/A002024

的一些学分

可以在Calculate Combination based on positionhttp://saliu.com/bbs/messages/348.html找到广义算法(对于元组而不是对),但它们似乎涉及在循环中计算组合。

编辑: a(来自同一来源)的更好的公式:

a = n - floor(0.5 + sqrt(2 * (t - index)));