3

So let's say i have T, T = 1200. I also have A, A is an array that contains 1000s of entries and these are numerical entries that range from 1000-2000 but does not include an entry for 1200.

What's the fastest way of finding the nearest neighbour (closest value), let's say we ceil it, so it'll match 1201, not 1199 in A.

Note: this will be run on ENTER_FRAME.

Also note: A is static.

Ahmed Nuaman
  • 12,662
  • 15
  • 55
  • 87

6 Answers6

3

It is also very fast to use Vector.<int>instead of Arrayand do a simple for-loop:

var vector:Vector.<int> = new <int>[ 0,1,2, /*....*/ 2000];

function seekNextLower( searchNumber:int ) : int {
    for (var i:int = vector.length-1; i >= 0; i--) {
        if (vector[i] <= searchNumber) return vector[i];
    }
}


function seekNextHigher( searchNumber:int ) : int {
    for (var i:int = 0; i < vector.length; i++) {
        if (vector[i] >= searchNumber) return vector[i];
    }
}

Using any array methods will be more costly than iterating over Vector.<int> - it was optimized for exactly this kind of operation.

weltraumpirat
  • 22,544
  • 5
  • 40
  • 54
2

If you're looking to run this on every ENTER_FRAME event, you'll probably benefit from some extra optimization.

If you keep track of the entries when they are written to the array, you don't have to sort them.

For example, you'd have an array where T is the index, and it would have an object with an array with all the indexes of the A array that hold that value. you could also put the closest value's index as part of that object, so when you're retrieving this every frame, you only need to access that value, rather than search.

Of course this would only help if you read a lot more than you write, because recreating the object is quite expensive, so it really depends on use.

You might also want to look into linked lists, for certain operations they are quite a bit faster (slower on sort though)

Daniel
  • 34,125
  • 17
  • 102
  • 150
  • I agree, an object is the way forward, shame that we can't have vector based objects, it was just performance and lookup I was worried about, but alas it works, even though a few are missed here and there, it doesn't matter as they're a millisecond each. – Ahmed Nuaman Apr 04 '12 at 16:19
  • Do me a favor and compare these suggestions to a simple loop over `Vector.` - I would bet it's still the fastest solution. – weltraumpirat Apr 04 '12 at 16:35
1

You have to read each value, so the complexity will be linear. It's pretty much like finding the smallest int in an array.

var closestIndex:uint;
var closestDistance:uint = uint.MAX_VALUE;
var currentDistance:uint;
var arrayLength:uint = A.length;

for (var index:int = 0; index<arrayLength; index++)
{
  currentDistance = Math.abs(T - A[index]);
  if (currentDistance < closestDistance || 
        (currentDistance == closestDistance && A[index] > T)) //between two values with the same distance, prefers the one larger than T
  {
    closestDistance = currentDistance;
    closestIndex = index;
  }
}

return T[closestIndex];
Kodiak
  • 5,978
  • 17
  • 35
  • This is quite heavy for running on `ENTER_FRAME`, I was hoping to avoid loops as A could be 1000s – Ahmed Nuaman Apr 04 '12 at 15:53
  • Well, if your array changes on every iteration of the test, you have no choice but to read each value at least once. Linearity is the best you can achieve here. – Kodiak Apr 04 '12 at 15:55
  • I hadn't seen your comments below the question ; if the array is static, there may be a better solution – Kodiak Apr 04 '12 at 15:56
1

Since your array is sorted you could adapt a straightforward binary search (such as explained in this answer) to find the 'pivot' where the left-subdivision and the right-subdivision at a recursive step bracket the value you are 'searching' for.

Community
  • 1
  • 1
High Performance Mark
  • 77,191
  • 7
  • 105
  • 161
0

Just a thought I had... Sort A (since its static you can just sort it once before you start), and then take a guess of what index to start guessing at (say A is length 100, you want 1200, 100*(200/1000) = 20) so guess starting at that guess, and then if A[guess] is higher than 1200, check the value at A[guess-1]. If it is still higher, keep going down until you find one that is higher and one that is lower. Once you find that determine what is closer. if your initial guess was too low, keep going up.

This won't be great and might not be the best performance wise, but it would be a lot better than checking every single value, and will work quite well if A is evenly spaced between 1000 and 2000.

Good luck!

M. Laing
  • 1,607
  • 11
  • 25
0
public function nearestNumber(value:Number,list:Array):Number{
    var currentNumber:Number = list[0];
    for (var i:int = 0; i < list.length; i++) {
        if (Math.abs(value - list[i]) < Math.abs(value - currentNumber)){
            currentNumber = list[i];
        }
    }
    return currentNumber;
}