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.
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.
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)
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];
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.
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!
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;
}
Related
I've tried using hitTestObject(), but that seems to require that I make the call using a specific instance, rendering it more or less useless unless I want to have dozens of objects making dozens of collision checks each frame, which just seems wasteful and annoying to implement.
Is there any way to do collision check based on class rather than instance?
Maybe Something equivalent to this:
http://docs.yoyogames.com/source/dadiospice/002_reference/movement%20and%20collisions/collisions/place_meeting.html
Alternatively, is there any function that returns whatever a list of objects that share overlapping coordinates with the one I'm checking?
If I'm understanding your question correctly, you have several objects of that same class that each need to check for collisions against each other?
Yes, you would have to go through each object and perform a collision check against the other objects. I suppose you could write a hitTestClass function yourself, but behind the scenes it would still be the same. As far as implementing it, it's not so bad:
for( var i:int = 0; i < asteroids.length -1; ++i )
{
var a:Asteroid = asteroids[ i ];
for( var j:int = i+1; j < asteroids.length; ++j )
{
var b:Asteroid = asteroids[ j ];
var isColliding:Boolean = a.hitTestObject( b );
//Code here to do whatever in the case of collision
}
}
If computational speed becomes a concern, then there are broad-phase collision detection techniques to chunk down the time. Quad trees are one example.
If I assign a Vector like
var vec1:Vector.<Number> = new Vector.<Number>(3);
vec1 = (1,2);
the result of vec1.length is 3. Is there any built-in method to return the number of the elements actually present in the vector?
I'm an ActionScript noob so any help would be appreciated.
Well you can solve your problem by creating an empty vector instead of a defining the vector size at the time of its declaration and then you gradually add and remove elements to the vector. In this way you will always get the total number of elements inside the vector when you call vector.length
For example:
var vec1:Vector.<Number> = new Vector.<Number>();
vec1.push(5);
vec1.push(6,7);
vec1.pop();
Then vec1.length would give you 2.
Its been awhile, heres the reference: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/Vector.html
I believe the .length is the normal way to check its length. If you want to check for "empty elements" you will need to loop through it with a loop.
I don't remmember the exact syntax, but to loop, do something like:
int count = 0;
for (int i = 0; i < vec.length; i++) {
if (vec[i] == ... );
count++;
}
I'm trying to work out a way for my game to recognise when two instances of this array have the same x or y position as another and if so to move re randomize the position of one of the instances.:
for(i=0;i<8;i++) {
PlatformInstance[i] =new Platform();
PlatformInstance[i].y= Math.random()*900;
PlatformInstance[i].x= Math.random() *1500;
stage.addChild(PlatformInstance[i]);
}
the problem i have is that the code i try detects that one platform instance has the same position as the SAME platform instance, and will constantly re randomize the position.
is there a way to differentiate between different instances?
thanks very much in advance.
EDIT
The only code i could think of was running an if statement in a loop to see whether
If (PlatformInstance[i].y == PlatformInstance[i].y)
Obviously this wouldnt work and thinking of it i know it wouldn't however i was wondering if there was a way to do:
If (Platforminstance[i].y == "other" Platforminstance[i].y
or some other words to that effect
You don't seem to have a clear understanding of how arrays work, I recommend looking into that. Something like this should work
i=0;
j=0;
while( i < PlatformInstance.length -1) {
j++;
if (j == PlatformInstance.length) {
i++;
j=i+1;
}
if( (Math.abs(PlatformInstance[i].x - PlatformInstance[j].x) < platformWidth)
|| (Math.abs(PlatformInstance[i].y - PlatformInstance[j].y) < platformHeight) ) {
PlatformInstance[j].y= Math.random()*900;
PlatformInstance[j].x= Math.random() *1500;
//Now you'll have start checking at the beginning again since you moved one
i=0;
j=0;
continue;
}
}
Also as it was previously mentioned, PlatformInstance is not a good name for an array. Only Class names should start with a capital letter. And an array of platforms should not be called an "instance". I would recommend changing it to simply platforms
According to Adobe, a reverse for loop is the fastest way to do a loop:
http://www.adobe.com/devnet/flash/articles/optimizing-flash-performance.html
I could not create a test where a reverse for loop was consistently faster than a normal loop, but I'm assuming Adobe knows Flash. Does anyone know under what circumstance this is true and why?
You don't have to recalculate the length of an array or Vector if you iterate backwards.
for(var i:int = list.length; i > 0; i--)
// -------------^^^^^^^^^^^ Length is calculated once for the start value.
Versus:
for(var i:int = 0; i < list.length; i++)
// --------------------^^^^^^^^^^^ Length is calculated for each iteration.
FYI, the difference is speed is negligible. I personally use whichever is more readable to me (the former).
That aside - if you are iterating over a collection where all objects are of the same type, you should be using a for each loop. This is much faster, more readable and more logical than either of the above. The reason that it is faster is because no type conversion is required at each iteration - the type is set when you define the loop:
var strings:Array = ["a","b","c"];
for each(var i:String in strings)
{
trace(i);
}
Marty is correct: if you use a reverse for loop you have to get the length value only once. However, this can be easily achieved through a normal for loop, and assigning the length to a variable beforehand.
A further optimized reverse for loop would look like:
for(var i:int = list.length; i--;)
which results in looping through every element in the array in reverse without a third for loop argument.
Is there an easier way to fill an actionscript array with a range of years (let's say 1900 to 2000) than by using a for loop? I thought there'd be some sort of range function, but I can't find it.
Nope. A loop is the way to go.
var years:Array = [];
for (var i:int = 1900; i < 2000; i++) years.push(i);
[!] FAR WORSE SOLUTION THAN THE ACCEPTED ONE [!]
An alternative just for the heck of it:
var years : Array = new Array( 100 ).map( function( item : *, index : int, arr : Array ){
return index + 1900;
} );
-- EDIT --
This was in no way a serious attempt for a better solution, since IT'S NOT!
It's slower and more memory consuming. I just wondered whether it would be possible with Array#map and found it is. Since I didn't know where to dump this snippet, I posted it here.