Equally Weighted, Non-Repeating Random Integer [AS3]

Subscribe to Equally Weighted, Non-Repeating Random Integer [AS3] 38 posts

Sign in to reply


 
avatar for Mond Mond 699 posts
Flag Post

var chosen:Array = new Array();

for (var i:int = 0; i < 25; i++) trace(i, EquallyWeighted_NonRepeatingRandom(20, chosen));



function EquallyWeighted_NonRepeatingRandom(arrayLength:uint, chosen:Array):int
{
	var theNum:int = 0;
		
	if (chosen.length == arrayLength) return -1;
	if (!arrayLength) return -1;
	do
	{
		theNum = Math.round(Math.random() * (arrayLength + 2));

	}
	while ((theNum == 0) || (theNum == arrayLength + 2) || (chosen.indexOf(theNum - 1) != -1));
		
	return chosen.push(--theNum);
	
} //end of function EquallyWeighted_NonRepeatingRandom

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 12
12 13
13 14
14 15
15 16
16 17
17 18
18 19
19 20
20 -1
21 -1
22 -1
23 -1
24 -1

This algorithm should be choosing randomly & not repeating any choices, but the output is in order and I don’t know why. Does anybody see where my code is incorrect?

EDIT: I added this line of code for debug:


for (i = 0; i < chosen.length; i++) trace("chosen[" + i + "]:" + chosen[i]);

chosen[0]:8
chosen[1]:13
chosen[2]:14
chosen[3]:19
chosen[4]:15
chosen[5]:17
chosen[6]:18
chosen[7]:16
chosen[8]:2
chosen[9]:7
chosen[10]:1
chosen[11]:3
chosen[12]:0
chosen[13]:4
chosen[14]:20
chosen[15]:11
chosen[16]:12
chosen[17]:6
chosen[18]:5
chosen[19]:10

The elements in the chosen array are random! WTH…..I return the return from the push!?

EDIT2: I found it, the push returns the length of the chosen array, I thought it returned whatever was pushed.

 
avatar for Wordblind Wordblind 1053 posts
Flag Post

Array.push(num) returns the new length of the array.

 
avatar for Mond Mond 699 posts
Flag Post

Here is the corrected code with its corresponding output:


var chosen:Array = new Array();

for (var i:int = 0; i < 25; i++) trace(i, EquallyWeighted_NonRepeatingRandom(20, chosen));
trace();
for (i = 0; i < chosen.length; i++) trace("chosen[" + i + "]:" + chosen[i]);



function EquallyWeighted_NonRepeatingRandom(arrayLength:uint, chosen:Array):int
{
	var theNum:int = 0;
		
	if (chosen.length == arrayLength + 1) return -1;
	if (!arrayLength) return -1;
	do
	{
		theNum = Math.round(Math.random() * (arrayLength + 2));

	}
	while ((theNum == 0) || (theNum == arrayLength + 2) || (chosen.indexOf(--theNum) != -1));
		
	chosen.push(theNum);
	return theNum;

} //end of function EquallyWeighted_NonRepeatingRandom

0 5
1 2
2 1
3 14
4 3
5 11
6 18
7 20
8 15
9 8
10 0
11 13
12 7
13 17
14 9
15 12
16 16
17 6
18 4
19 10
20 19
21 -1
22 -1
23 -1
24 -1

chosen[0]:5
chosen[1]:2
chosen[2]:1
chosen[3]:14
chosen[4]:3
chosen[5]:11
chosen[6]:18
chosen[7]:20
chosen[8]:15
chosen[9]:8
chosen[10]:0
chosen[11]:13
chosen[12]:7
chosen[13]:17
chosen[14]:9
chosen[15]:12
chosen[16]:16
chosen[17]:6
chosen[18]:4
chosen[19]:10
chosen[20]:19

I realize this code could become “jammed up” in the do..while statement. Does anyone have a better way of doing this?

 
avatar for CuriousGaming CuriousGaming 392 posts
Flag Post

What do you want to happen after all 20 numbers have come out? Repeat the sequence? A default number, like -1?

An alternative method is to put sequential numbers into an array, and mix them all up:

(repeat 50 times)
- swap (random array element with another random array element)

Then use array.pop until the array is empty.

 
avatar for Wordblind Wordblind 1053 posts
Flag Post

The standard method is similar, except only one side of the swap is random.

function shuffle(cards:Array) {
	var temp:*, rand:uint, index:uint, length:uint = cards.length;
	for(index in cards) {
		rand = Math.floor(Math.rand() * length);
		temp = cards[index];
		cards[index] = cards[rand];
		cards[rand] = temp;
	}		
}
 
avatar for Mond Mond 699 posts
Flag Post

Thanks ya’ll. Math.floor, which I have never looked at before, avoids the unequal weighting of Math.round. It cuts much fat off my original idea. Almost a one-liner:


var chooseFrom:Array = new Array();
for (var i:int = 0; i < 15; i++) chooseFrom.push(i);
for (i = 0; i < 20; i++) trace(i, NonRepeatingRandom(chooseFrom));


function NonRepeatingRandom(chooseFrom:Array):int
{
	if (!chooseFrom.length) return -1;
	return chooseFrom.splice(Math.floor(Math.random() * chooseFrom.length), 1)[0];
	
} //end of function NonRepeatingRandom

0 6
1 13
2 10
3 3
4 1
5 2
6 11
7 14
8 5
9 4
10 12
11 9
12 7
13 8
14 0
15 -1
16 -1
17 -1
18 -1
19 -1

 
avatar for UnknownGuardian UnknownGuardian 6208 posts
Flag Post

Of you could have just cast the number to an int. If would look like this

function NonRepeatingRandom(chooseFrom:Array):int
{
	if (!chooseFrom.length) return -1;
	return chooseFrom.splice((int)(Math.random() * chooseFrom.length), 1)[0];
	
} //end of function NonRepeatingRandom

Where (int) replaced Math.floor You also might check on efficiency if its a big deal.

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post
function NonRepeatingRandom(chooseFrom:Array):int
{
	if (!chooseFrom.length) return -1;
	return chooseFrom.splice(int(Math.random() * chooseFrom.length), 1)[0];
	
} //end of function NonRepeatingRandom

I don’t think you need the [0] on the end either

 
avatar for UnknownGuardian UnknownGuardian 6208 posts
Flag Post

I kept the ( and ) for clarification. When Flash Player runs the code, the () won’t be there.

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post

I thought you’d been playing with C and gotten confused. I still don’t think you need the [0] at the end of splice.

 
avatar for UnknownGuardian UnknownGuardian 6208 posts
Flag Post

Ya, I believe the [0] was a typo that the thread owner accidentally put in.

 
avatar for the8bitYoda the8bitYoda 79 posts
Flag Post
Originally posted by UnknownGuardian:

Ya, I believe the [0] was a typo that the thread owner accidentally put in.

The splice() method returns an array, since the function expects an int to be returned the [ 0] is necessary.

 
avatar for UnknownGuardian UnknownGuardian 6208 posts
Flag Post
Originally posted by the8bitYoda:
Originally posted by UnknownGuardian:

Ya, I believe the [0] was a typo that the thread owner accidentally put in.

The splice() method returns an array, since the function expects an int to be returned the [ 0] is necessary.

Ya, but isn’t the goal of the method to return a value? Doesn’t splice just return a value if you just get 1 index?

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post

I just tried it in FlashDevelop and it compiles fine.

 
avatar for the8bitYoda the8bitYoda 79 posts
Flag Post

I stand corrected, it does compile fine, so it is optional to have the [ 0] (but not wrong) in this case. The compiler apparently let’s you implicitly cast the returned Array to an int. splice() always returns an Array, which can be verified by:


//Assuming arr is an Array of integers

trace((arr.splice(1,1)) is Array); //true
trace((arr.splice(1,1)) is int); //false

trace((arr.splice(1,1))[ 0] is Array); //false
trace((arr.splice(1,1))[ 0] is int); //true

trace(int(arr)); //Returns the integer if arr has only one element, returns 0 otherwise.


Strange behavior I think…but interesting to know.

 
avatar for Draco18s Draco18s 5875 posts
Flag Post
Originally posted by UnknownGuardian:
Originally posted by the8bitYoda:
Originally posted by UnknownGuardian:

Ya, I believe the [0] was a typo that the thread owner accidentally put in.

The splice() method returns an array, since the function expects an int to be returned the [ 0] is necessary.

Ya, but isn’t the goal of the method to return a value? Doesn’t splice just return a value if you just get 1 index?

No, it returns an array length 1 with a single value at index 0.

 
avatar for UnknownGuardian UnknownGuardian 6208 posts
Flag Post

Ah, but its still works, as stated by the8bitYoda in the post above.

 
avatar for Draco18s Draco18s 5875 posts
Flag Post
Originally posted by UnknownGuardian:

Ah, but its still works, as stated by the8bitYoda in the post above.

If you convert it to an integer, then yes, it works, that’s what the int() function does, and one of the really neat things about figuring out what the integer value of something is is you can go “oh, that’s a 1 element array, I’ll return the integer value of the 0th index” because its the only integer that could represent that array meaningfully.

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post

You don’t have to convert it to an interger, it’s done so implicitly.

 
avatar for Draco18s Draco18s 5875 posts
Flag Post
Originally posted by SuperMarioJump:

You don’t have to convert it to an interger, it’s done so implicitly.

Type inference, or implicit typing, refers to the ability to deduce automatically the type of a value in a programming language.

Ta da, just because its converted implicitly doesn’t mean its not an array.

 
avatar for Mond Mond 699 posts
Flag Post

Maybe explicitly..because of the function’s return typing..so I tried it with Number:


var myNumber:Number = 1.0;

trace(myNumber);

var chooseFrom:Array = new Array();
for (var i:int = 14; i >= 0; i--) chooseFrom.push(i);
for (i = 0; i < 20; i++) trace(i, NonRepeatingRandom(chooseFrom));


function NonRepeatingRandom(chooseFrom:Array):Number
{
	if (!chooseFrom.length) return -1;
	return chooseFrom.splice(Math.floor(Math.random() * chooseFrom.length), 1);		//[0];
	
} //end of function NonRepeatingRandom

1
0 4
1 14
2 10
3 8
4 9
5 0
6 2
7 13
8 6
9 11
10 3
11 7
12 5
13 1
14 12
15 -1
16 -1
17 -1
18 -1
19 -1

But the trace call drops the decimal part.

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post

What decimal part?

 
avatar for Mond Mond 699 posts
Flag Post

1.0
0 4.0
1 14.0
2 10.0
3 8.0
4 9.0
5 0.0
6 2.0
7 13.0
8 6.0
9 11.0
10 3.0
11 7.0
12 5.0
13 1.0
14 12.0
15 -1.0
16 -1.0
17 -1.0
18 -1.0
19 -1.0

 
avatar for SuperMarioJump SuperMarioJump 303 posts
Flag Post

I’m not sure what the point you’re making here is.

 
avatar for Mond Mond 699 posts
Flag Post

var myNumber:Number = 1.0;

trace(myNumber, myNumber is Number);

var chooseFrom:Array = new Array();
for (var i:int = 5; i >= 0; i--) chooseFrom.push(i);
for (i = 0; i < 7; i++)
{
	myNumber = NonRepeatingRandom(chooseFrom);
	trace(i, myNumber, myNumber is Number);
}

function NonRepeatingRandom(chooseFrom:Array):Number
{
	if (!chooseFrom.length) return -1;
	return chooseFrom.splice(int(Math.random() * chooseFrom.length), 1);		//[0];
	
} //end of function NonRepeatingRandom

1 true
0 4 true
1 3 true
2 1 true
3 5 true
4 2 true
5 0 true
6 -1 true
    

Sign in to reply