我正在实现一个碰撞检测算法,用于存储单个八叉树节点中所有对象之间的距离。例如,如果节点中有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个变量,一个是索引,另一个是整个对象。节点。如果可能的话,我想创建一个没有任何循环或具有递归函数的函数,因为这将为我的碰撞检测算法实时运行。
答案 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;
的一些学分
可以在Calculate Combination based on position和http://saliu.com/bbs/messages/348.html找到广义算法(对于元组而不是对),但它们似乎涉及在循环中计算组合。
编辑: a(来自同一来源)的更好的公式:
a = n - floor(0.5 + sqrt(2 * (t - index)));