2

Please refer below code.

for (var i = 0; i < elements.length; i++) 
{
     //var element = elements[Math.floor(Math.random()*elements.length)];
     this.animateSymbol(elements[Math.floor(Math.random()*elements.length)]);
}

elements array contains list of svg elements(circle/path/ellipse etc). I want to select the random element from elements array.

it's return the same element in some cases I want to select the element randomly no need to select the same element again. Need to select different element from that array.

What's the problem ? Why its returning same index and same element ?

Thanks,

Siva

Rakesh Shetty
  • 4,548
  • 7
  • 40
  • 79
SivaRajini
  • 7,225
  • 21
  • 81
  • 128

5 Answers5

8

Random numbers are random. There's no guarantee you won't get the same random number twice. In fact, when you convert the random numbers to a limited range of integers, it's quite likely you will get the same number twice.

You can fix this by copying the array and then each time you get a value from the array, remove it. Let's also break out the code that generates the random index as a separate function; it's handy in other situations too:

// Return a random integer >= 0 and < n
function randomInt( n ) {
    return Math.floor( Math.random() * n );
}

var copy = elements.slice();
while( copy.length ) {
    var index = randomInt( copy.length );
    this.animateSymbol( copy[index] );
    copy.splice( index, 1 );
}

And just for fun, here's another way you could code that loop:

var copy = elements.slice();
while( copy.length ) {
    var index = randomInt( copy.length );
    this.animateSymbol( copy.splice( index, 1 )[0] );
}

Either one does the same thing. I kind of like the step by step approach for clarity, but it can be quite handy that the .splice() method returns an array of the element(s) you delete.

Here's a version of the code you can paste into the JavaScript console to test:

// Return a random integer >= 0 and < n
function randomInt( n ) {
    return Math.floor( Math.random() * n );
}

var elements = [ 'a', 'b', 'c', 'd', 'e' ];
var copy = elements.slice();
while( copy.length ) {
    var index = randomInt( copy.length );
    console.log( copy.splice( index, 1 )[0] );
}
console.log( 'Done' );

It's also worth a look at Xotic750's answer. It uses the Fisher-Yates shuffle which randomizes an array in place. This would likely be more efficient for a very lengthy array.

Community
  • 1
  • 1
Michael Geary
  • 28,450
  • 9
  • 65
  • 75
  • thanks for your reply. but i have checked in console window same random numbers will be printed in that window. why ? – SivaRajini Jun 11 '13 at 09:37
  • @SivaRajini - Not sure what you are referring to. I added a version of the code that's ready to paste into the JavaScript console. It seems to work OK. – Michael Geary Jun 11 '13 at 09:44
  • whether i can use your code in for loop for(var i=0; i – SivaRajini Jun 11 '13 at 09:47
  • @michael since you are removing the each element from array whether it will affect the for loop ? – SivaRajini Jun 11 '13 at 09:48
  • No, this code already *has* the loop in it. You don't need another loop around it. Paste that last example into the JavaScript console as-is and see how it works. – Michael Geary Jun 11 '13 at 09:57
  • sorry michael it still creates the problem. same it returns the same index. var index = randomInt( copy.length ); – SivaRajini Jun 11 '13 at 10:01
  • i have checked in console.log it will return same index value again and again using jquery 1.9.1 version – SivaRajini Jun 11 '13 at 10:02
  • I don't understand. This code has nothing to do with jQuery. Did you paste my last code sample into the JavaScript console exactly as it is? I tested it in Chrome, Firefox, and IE10. It works perfectly in all three, displaying the five letters in a random order each time I run it. – Michael Geary Jun 11 '13 at 10:05
2

So what you want is akin to a deck of cards, you shuffle them and take them one by one and therefore they are never repeated.

I would use something like the following for your problem, uses a standard Fisher-Yates shuffle.

function shuffle(obj) {
  var i = obj.length;
  var rnd, tmp;

  while (i) {
    rnd = Math.floor(Math.random() * i);
    i -= 1;
    tmp = obj[i];
    obj[i] = obj[rnd];
    obj[rnd] = tmp;
  }

  return obj;
}

var elements = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var randomised = elements.slice();
shuffle(randomised);

randomised.forEach(function(element) {
  console.log(element);
});
Xotic750
  • 22,914
  • 8
  • 57
  • 79
1

It is because you are using random there is no assurance that same number will not be repeated.

In your case I would suggest you to use some sort of shuffle to create a random order array.

You can find such method here

Community
  • 1
  • 1
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
1

Try capturing the random numbers generated and checking that you are not using them over again.

Here is a quick object I created for this purpose:

function PersistentRandom(exclusiveUpperBounds){
  this.spent = [];
  this.bounds = exclusiveUpperBounds;
}

PersistentRandom.prototype.getValue = function(){
    if(this.spent.length != this.bounds -1){
        var tmp = Math.floor(Math.random()* this.bounds);
        if(this.spent.indexOf(tmp) == -1){
            this.spent.push(tmp);
            return tmp;
        }else{
            return this.getValue();
        }
    }else{
        //If all numbers are used reset and start again
        this.spent = [];
        return this.getValue();
    }
};

//Usage
var pr = new PersistentRandom(11);

var x = 0;
while(x < 15){
   console.log(pr.getValue());
   x++;
}

Working Example http://jsfiddle.net/zasdj/

Kevin Bowersox
  • 93,289
  • 19
  • 159
  • 189
  • +1 for an alternative solution, could set 'this.spent.length = 0;´ rather than ´this.spent = [];´ as an improvement, but better than the removing element suggestions though a little over complicated I feel and relies on Array.indexOf (though shims are available). The bonus of this solution is the reset and start again. – Xotic750 Jun 11 '13 at 11:51
-1

So you want a random element each time? but never the same element twice?

try this:

for (var i = 0; i < elements.length; i++) {
     //var element = elements[Math.floor(Math.random()*elements.length)];
     var index = Math.floor(Math.random()*elements.length);         
     this.animateSymbol(elements[index]);   
     elements.splice(index, 1);
}

this will remove the item from array once it has been selected

d0mmmy
  • 405
  • 1
  • 3
  • 6
  • -1, this does not work because the elements array length gets shorter each time you remove an element from the array. The solution would be to reverse the for loop so that it counts downwards. http://jsfiddle.net/Xotic750/H8wbr/ – Xotic750 Jun 11 '13 at 11:28