passing random values from one array to another without repetitions - actionscript-3
I have an array (say 'origA') which contains 20 values and also another array (say "itemA" with only 1 value in it. I need to push any 10 random values of "origA" into "itemA". But i cannot push a same value which is already pushed into "itemA".
How can we do this?
You can create a copy of origA and remove from it the items you add to itemA:
Non optimized version:
var origA:Array = [1, 2, 3, 4, 5, 6, 7];
var itemA:Array = [0];
var copyA:Array = origA.concat();
var N:int = 10;
var n:int = Math.min(N, copyA.length);
for (var i:int = 0; i < n; i++) {
// Get random value
var index:int = Math.floor(Math.random() * copyA.length);
var value:int = copyA[index];
// Remove the selected value from copyA
copyA.splice(index, 1);
// Add the selected value to itemA
itemA.push(value);
}
trace(itemA);
//0,1,7,2,6,4,3,5
Optimized version (no calls to length, indexOf, splice or push inside the loop):
var origA:Array = [1, 2, 3, 4, 5, 6, 7];
var itemA:Array = [0];
var copyA:Array = origA.concat();
var copyALength:int = copyA.length;
var itemALength:int = itemA.length;
var N:int = 10;
var n:int = Math.min(N, copyALength);
for (var i:int = 0; i < n; i++) {
// Get random value
var index:int = Math.floor(Math.random() * copyALength);
var value:int = copyA[index];
// Remove the selected value from copyA
copyA[index] = copyA[--copyALength];
// Add the selected value to itemA
itemA[itemALength++] = value;
}
trace(itemA);
//0,2,5,7,4,1,3,6
Edit1: If your original array has only a few items, use my first version or any other solution in the other answers. But if it may have thousands items or more, then I recommend you use my optimized version.
Edit:2 Here is the time taken to copy 1,000 randomly chosen items from an array containing 1,000,000 items:
All other versions: 2000ms
Optimized version: 12ms
Optimized version without cloning the original array: 1ms
// Define how many random numbers are required.
const REQUIRED:int = 10;
// Loop until either the original array runs out of numbers,
// or the destination array reaches the required length.
while(origA.length > 0 && itemA.length < REQUIRED)
{
// Decide on a random index and pull the value from there.
var i:int = Math.random() * origA.length;
var r:Number = origA[i];
// Add the value to the destination array if it does not exist yet.
if(itemA.indexOf(r) == -1)
{
itemA.push(r);
}
// Remove the value we looked at this iteration.
origA.splice(i, 1);
}
Here's a real short one. Remove random items from the original array until you reach MAX, then concat to the target Array:
const MAX:int = 10;
var orig:Array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
var target:Array = [];
var tmp:Array = [];
var i : int = -1;
var len : int = orig.length;
while (++i < MAX && len > 0) {
var index:int = int( Math.random()*len );
tmp[i] = orig[index];
orig[index] = orig[--len];
}
target = target.concat(tmp);
EDIT
Adopted #sch's way of removing items. It's his answer that should be accepted. I just kept this one for the while-loop.
Related
How to add numbers in array via a loop in AS3?
var arr: Array = [1, 2, 3, 4, 5, 6, 7, 8, 9]; I can add these numbers to an array separately like this but how do I add 1 to 50 at once instead of typing it all the way through? for (var i:Number=1; i<=50;i++){ var arr:Array(i) = [i]; } function randomize(a: * , b: * ): int { return (Math.random() > .5) ? 1 : -1; } trace(arr.sort(randomize)); I am trying to implement something like this. Thank you.
Pretty simple. You can address the Array's elements via square bracket notation. Works both ways: // Assign 1 to 10-th element of Array A. A[10] = 1; // Output the 10-th element of A. trace(A[10]); Furthermore, you don't even need to allocate elements in advance, Flash Player will automatically adjust the length of the Array: // Declare the Array variable. var A:Array; // Initialize the Array. You cannot work with Array before you initialize it. A = new Array; // Assign some random elements. A[0] = 1; A[3] = 2; // This will create the following A = [1, null, null, 2] So, your script is about right: // Initialize the Array. var arr:Array = new Array; // Iterate from 1 to 50. for (var i:int = 1; i <= 50; i++) { // Assign i as a value to the i-th element. arr[i] = i; } Just keep in mind that Arrays are 0-based, so if you forget about index 0 it will remain unset (it will be null).
Simplest way to prevent math.random form selecting the same number twice (AS3)
I have a random number variable defined as below var rannum:Number = Math.floor(Math.random()*50+1); Then I have a trigger that calls for a new random number everytime a button is clicked ranbtn.addEventListener(MouseEvent.CLICK, reran); function reran (event:MouseEvent):void { rannum = Math.floor(Math.random()*50+1); } I would like to prevent the same random number from being selected until all the numbers have been selected and then possibly start over? I found a few threads like this one but none of them were specifically what I needed
You need to create an array of the possible values and each time you retrieve a random index from the array to use one of the values, you remove it from the array.Here you have an easy example with javascript. var uniqueRandoms = []; var numRandoms = 50; function makeUniqueRandom() { // refill the array if needed if (!uniqueRandoms.length) { for (var i = 0; i < numRandoms; i++) { uniqueRandoms.push(i); } } var index = Math.floor(Math.random() * uniqueRandoms.length); var val = uniqueRandoms[index]; // now remove that value from the array uniqueRandoms.splice(index, 1); return val; } I've found another option, You can declare an array of Integers:[1,2,3,4...50] and sort them randomly. var sorted:Array = []; for(var i:int = 0; i < 50; i++){ sorted.push(i); } //I'm making a copy of sorted in unsorted var unsorted:Array = sorted.slice(); //Randomly sort while(sorted.join() == unsorted.join()){ unsorted.sort(function (a:int, b:int):int { return Math.random() > .5 ? -1 : 1; }); }
If you get a selected num, you can add one until it is not selected.
Create a list of integers from 1 to 50. Pick a random integer from the list and remove it. When there are no more integers left (after 50 picks), repeat step 1. Code: function createRangeOfIntegers(from:int, to:int):Vector.<int> { if (from >= to) throw new ArgumentError("Invalid arguments"); var integers:Vector.<int> = new <int>[]; for (var i:int = from; i <= to; i++) { integers.push(i); } return integers; } function getRandomInteger(integers:Vector.<int>):int { var index:int = Math.random() * integers.length; var integer:int = integers.splice(index, 1)[0]; return integer; } Example: // create all the possible integers var integers:Vector.<int> = createRangeOfIntegers(1, 50); // select a random integer var random:int = getRandomInteger(integers); // When you've selected all integers you can start over if (integers.length == 0) integers = createRangeOfIntegers(1, 50);
how to select random objects to display on a fixed position in adobe flash
I am working on a flash project and I want to select random objects from a number of objects. For example if I have 15 objects and I want to randomly select just 4 objects and display them on the stage at fixed position. I have searched different forums and the problems discussed on different forums are about changing the random position of objects Note that I don't want to randomize objects position on stage I want to select random objects from multiple objects I have no idea how to do this. Please help me if anyone can.
The best thing is to put those objects into array, and then get random element from it. Here is a quick example: var objects:Array = new Array[obj1, obj2, obj3, obj4, obj5]; var random:Object = objects.splice(int(Math.random() * objects.length), 1)[0]; Have in mind that these are predefined objects - you must populate the array by yourself. Another thing is that this splices the array, which means it removes items from it. Good luck!
Similar to Andrey Popov's answer but step by step instead of one line: //an array of symbols var objects:Array = new Array(red,blue,green); //Number you need var needed:Number = 2; for (var i:Number = 0;i< needed;i++){ var randomPos = int(Math.random()*objects.length); //Insert fixed position here objects[i].x = 0; //remove object from array objects.splice(i,1); }
You need some algorithm to get unique N objects from the object pool. For example try this one: function getItemsFrom(list:Array, count:uint):Array { var result:Array = []; var needed:uint = count; var available:uint = list.length; while (result.length < count) { if (Math.random() < needed / available) { result.push(list[available - 1]); needed--; } available--; } return result; } //Simple test, and some results are: [16,9,7,5], [14,13,10,1], etc var test:Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; for(var i:uint = 0; i < 20; ++i){ trace(getItemsFrom(test, 4)); } After you will get array of objects, place them where you want. //Place items vertically at (5,5); var startX:int = 5; var startY:int = 5; //Vertical padding between items var paddingY:int = 10; //Current Y position var posY:int = startY; //Ger 2 random unique items from the given collection "someListWithItems" var itemsToPlace:Array = getItemsFrom(someListWithItems, 2); var item:DisplayObject, i:uint, len:uint = itemsToPlace.length; for (i; i < len; ++i) { item = itemsToPlace[i]; item.x = startX; item.y = posY; //Offset current position on height of object, plus padding posY += item.height + paddingY; //Add item to the display list addChild(item); }
Get each element from array as string actionscript3
I have an array like var test:Array = new Array("a", "b", "c"); How can I write a method to get one element and make it be string each time when I call this method. i.e when I call the method, it should return only 'a' and next time return only 'b' and so on.
You can use function shift of Array,here is a link about the function array shift var test:Array = new Array("a", "b", "c"); var firstLetter:String = test.shift();//"a" var secondLetter:String = test.shift();//"b" var thirdLetter:String = test.shift();//"c"
#Pan's answer is correct, but I feel the need to flag the fact that shift() ignites an extremely slow process of re-indexing your entire array. It's not something you need to concern yourself with with small arrays like in your example, but for larger arrays there's a significant performance boost if you reverse() the array first and then use pop(). I'll create a performance comparison below. Set up our test arrays: var shiftCopy:Array = []; var popCopy:Array = []; for(var i:int = 0; i < 100000; i++) { var rand:Number = Math.random() * i; shiftCopy.push('a' + rand); popCopy.push('a' + rand); } Run the tests: // Using shift. var t:int = getTimer(); while(shiftCopy.length > 0) shiftCopy.shift(); trace(getTimer() - t); // Using reverse and pop. t = getTimer(); popCopy.reverse(); while(popCopy.length > 0) popCopy.pop(); trace(getTimer() - t); My results: shift: 1651ms pop: 19ms
Sorting a multidimentional array and moving all the same values to the last array in as3
I'm currently working on a primitive graphic for the inventory that shows how much space is currently taken and how much theres left. It does this by showing 5 rows of 20 squares (Gray = free space, yellow =space taken). Now, my problem is that I want to find all values that are 1 and put them in the last array. So that the array goes from: var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; to var myMap:Array = [ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ]; Basically I want to sort the entire array so that all zero's come first and all 1's get put in the last one till its full, then the second last one and so on. I'm away I will most likely need an algorithm for this but I am hoping theres an easier way to do this. I'm usually a C# developer so AS3 is not my strongest suit. hopefully someone understood what I ment and can help me, its 4:30 am, so I might not be as lucid as I'd like. ~Thanks EDIT Added the code so people can get more of a informed look: Keep in mind most of the names and so on are placeholders, currently I just need it working. private function _showInventoryGraphic() { var mapWidth = 20; var mapHeight = 5; var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; var posX:int = 15; var posY:int = 15; var startPosY:int = 250; for (var i:int=0; i<mapHeight; i++) { for (var u:int = 0; u < mapWidth; u++) { if (myMap[i][u] == 0) { var grayRect:Shape = new Shape; grayRect.graphics.beginFill(0x808080); grayRect.graphics.drawRect(posX*u, startPosY, 10,10); grayRect.graphics.endFill(); addChild(grayRect); } if (myMap[i][u] == 1) { var yellowRect:Shape = new Shape; yellowRect.graphics.beginFill(0xFFFF00); yellowRect.graphics.drawRect(posX*u, startPosY, 10,10); yellowRect.graphics.endFill(); addChild(yellowRect); } } startPosY += posY; } }
After trying it out from a few different approaches, I think the biggest challenge is the fact that it is a 2d array. The first attempt was similar, but like quicksort, where there were 2 pointers at either end of the 2d array and they moved 'inwards' looking for a scenario where the first pointer was a '1' and the last pointer was a '0' and swapping the two, until the pointers met in the 'middle'. I gave up on this method while trying to properly decrement and increment the linear counters in a 2d array. =b The second attempt was by keeping track of the last index in the array that was as empty and the last index in the array that was full. Then within the addItem and removeItem methods I'd put the item in the correct spot and update the counters to avoid a sort. This seemed to work, but maybe a bit messy and perhaps harder to maintain in the future. It sorts the result in separate arrays. In the third attempt for a 'direct sort', I think the most simple way then is just flatten the array apart in to a 2d array, sort it, then rejoin it. You would be correct in that this is not the most efficient method, though one thing to consider is how often this needs to run and how efficient it really needs to be given the context. I think a 2d array of only 100 elements is not too big of an issue to use an inefficient sort method? Maybe someone else can come up with better/cleaner methods. =b Below is code for what I mentioned. Second attempt (Note: I did not test this one fully, but it seemed to work): import flash.geom.Point; var unsortedMap:Array = [ [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0] ]; var sortedMap:Array = [ [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0,0,0] ]; var rowSize:int = unsortedMap[0].length; var rowCount:int = unsortedMap.length; // points to the last index at start, since want to have filled items at the 'end' var emptySpotPointer:Point = new Point(rowSize - 1, rowCount - 1); var fullSpotPointer:Point = new Point(rowSize - 1, rowCount - 1); function print2dArray(prefix:String, input:Array):void { trace(prefix); for(var row:String in input){ trace("\t"+ input[row]); } trace(""); } function addItem(inputX:int, inputY:int):void { if(unsortedMap[inputY][inputX] == 1){ trace("addItem() - Already an item here: "+ inputX +", "+ inputY); return; } trace("addItem() - Adding an item: "+ inputX +", "+ inputY); unsortedMap[inputY][inputX] = 1; sortedMap[emptySpotPointer.y][emptySpotPointer.x] = 1; fullSpotPointer.x = emptySpotPointer.x; fullSpotPointer.y = emptySpotPointer.y; if(emptySpotPointer.x == 0){ emptySpotPointer.x = rowSize - 1; emptySpotPointer.y--; } else { emptySpotPointer.x--; } } function removeItem(inputX:int, inputY:int):void { if(unsortedMap[inputY][inputX] == 0){ trace("removeItem() - No item here to remove: "+ inputX +", "+ inputY); return; } trace("removeItem() - Removing an item here: "+ inputX +", "+ inputY); unsortedMap[inputY][inputX] = 0; sortedMap[fullSpotPointer.y][fullSpotPointer.x] = 0; if(fullSpotPointer.x == (rowSize - 1)){ fullSpotPointer.x = 0; fullSpotPointer.y++; } else { fullSpotPointer.x++; } } // testing stuff here // ----------------------------------------------------------------- function addRandomitems():void { var randomX:int = Math.floor(Math.random() * rowSize); var randomY:int = Math.floor(Math.random() * rowCount); addItem(randomX, randomY); } function removeRandomitems():void { var randomX:int = Math.floor(Math.random() * rowSize); var randomY:int = Math.floor(Math.random() * rowCount); removeItem(randomX, randomY); } print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); trace("Test: Adding items now ---------------------"); var counter:int = 0; for(counter = 0; counter < 50; counter++){ addRandomitems(); print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); } trace("Test: Removing items now ---------------------"); for(counter = 0; counter < 50; counter++){ removeRandomitems(); print2dArray("unsortedMap", unsortedMap); print2dArray("sortedMap", sortedMap); } Third attempt: import flash.utils.getTimer; var myMap:Array = [ [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0], [1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0], [1,0,0,0,0,0,1,1,0,1,1,0,0,1,1,0,0,0,0,0] ]; function sort(inputArray:Array):Array { var rowSize:int = inputArray[0].length; var flat:Array = new Array(); // flattening the array here for(var row:String in inputArray){ flat = flat.concat(inputArray[row]); } flat.sort(); var result:Array = new Array(); // recreating the original array here by cutting out 'rowSize' chunks and reforming the 2d array while(flat.length > 0){ result.push(new Array(flat.splice(0, rowSize))); } return result; } // testing var startTimer:int = flash.utils.getTimer(); for(var counter:int = 0; counter < 10000; counter++){ sort(myMap); } // Running the above 10,000 times takes 1836ms for me. trace(flash.utils.getTimer() - startTimer); // just to see the output: trace(sort(myMap).join("\n"));