1

I am implementing a collision detection algorithm stores the distance between all the objects in a single octree node. For instance if there are 4 objects in the node, there is a distance between objects 1&2, 1&3, 1&4, 2&3, 2&4 and 3&4. The formula for the total number of pairs is t = n * (n-1) / 2, where t is the total number of pairs and n is the number of objects in a node.

My question is, how do I convert from a position in the list to a pair of objects. For instance, using the above list of pairs, 3 would return the pair 2&3.

To save space in memory, the list is just a list of floats for the distance instead of containing distance and pointers to 2 objects.

I am unsure how to mathematically convert the single list index to a pair of numbers. Any help would be great. I am hoping to be able to break this down to 2 functions, the first returns the first object in the pair and the second returns the second, both the functions taking 2 variables, one being the index and the other being the total objects in the node. If possible I would like to make a function without any looping or having a recursive function because this will be run in real time for my collision detection algorithm.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Chase Walden
  • 1,252
  • 1
  • 14
  • 31
  • Do I understand correctly that you're looking to 'index' the sorted nC2 of your node list ( (8 2) being being the max, (2 2) being the min assuming there are always children) so that given index 'n' you want the pair from that sorted combinations list ? Is that right ? – WhozCraig Nov 28 '12 at 07:49
  • @WhozCraig: No the list is just an array of arbitrary distance values corresponding to a pair of two objects. To cut the memory consumption, I just store the floating point value representing distance, not the sorted pairs, instead of the distance and 2 pointers to the objects, which would easily double or even triple the memory consumption. The issue is to figure out which two objects the array entry represents by mathematically calculating the pairs in ascending order, the first pair item having priority. – Chase Walden Nov 28 '12 at 08:00
  • So [d1, d2, d3, d4...] would be the distances between [1&2, 1&3, 1&4, 2&3....] and, given a distance, return which [n&m] goes with it? Am i getting closer ? – WhozCraig Nov 28 '12 at 08:07
  • @WhozCraig: Yeah, pretty much except given a index in the array (i.e. 1 would return 1&2 and 3 would return 1&4) – Chase Walden Nov 28 '12 at 08:13
  • @WhozCraig: The only thing is that the number of objects can change so it needs to dynamically calculate the pair. I don't want to store the pair in an array. Doing that will be useless to conserve memory consumption – Chase Walden Nov 28 '12 at 08:19

2 Answers2

4

Better ordering

I suggest using colexicographical order, as in that case you won't have to supply the total number of objects. Order your pairs like this:

 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, …

You'll ve able to extend this list to infinite length, so you can know the index of any pair without knowing the number of items. This has the benefit that when you add new items to your data structure, you'll only have to append to your arrays, not relocate existing entries. I've adjusted the indices to zero-based, as you tagged your question C++ so I assume you'll be using zero-based indexing. All my answer below assumes this ordering.

You can also visualize the colex ordering like this:

 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
⋮   ⋮                 ⋱

Pair to single index

Let us first turn a pair into a single index. The trick is that for every pair, you look at the second position and imagine all the pairs that had a lesser number in that position. So for example for the pair 2&4 you first count all the pairs where the second number is less than 4. This is the number of possible ways to choose two items from a set of 4 (i.e. the numbers 0 through 3), so you could express this as a binomial coefficient 4C2. If you evaluate it, you end up with 4(4−1)/2=6. To that you add the first number, as this is the number of pairs with lower index but with the same number in the second place. For 2&4 this is 2, so the overall index of 2&4 is 4(4−1)/2+2=8.

In general, for a pair a&b the index will be b(b−1)/2+a.

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

Single index to pair

One way to turn the single index i back into a pair of numbers would be increasing b until b(b+1)/2 > i, i.e. the situation where the next value of b would result in indices larger than i. Then you can find a as the difference a = ib(b−1)/2. This approach by incrementing b one at a time involves using a loop.

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);
}

You could also interpret b(b−1)/2 = i as a quadratic equation, which you can solve using a square root. The real b you need is the floor of the floating point b you'd get as the positive solution to this quadratic equation. As you might encounter problems due to rounding errors in this approach, you might want to check whether b(b+1)/2 > i. If that is not the case, increment b as you would do in the loop approach. Once you have b, the computation of a remains the same.

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);
}

Sequential access

Note that you only need to turn indices back to pairs for random access to your list. When iterating over all pairs, a set of nested loops is easier. So instead of

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
}

you'd better write

for (int i = 0, b = 1; b != n; ++b) {
  for (int a = 0; a != b; ++a) {
    // do stuff
    ++i;
  }
}
MvG
  • 57,380
  • 22
  • 148
  • 276
2

Based on my understanding of the question, one way to get a pair a&b (1-based, 2&3 in your example) from the index (0-based, 3 in your example) and the number of objects n (4 in your example) is:

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;

Some credits to http://oeis.org/A002024

Generalized algorithms (for tuples rather than pairs) can be found at Calculate Combination based on position and http://saliu.com/bbs/messages/348.html, but they seem to involve calculating combinations in a loop.

Edit: a nicer formula for a (from the same source):

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