Random Image with No Repeats

I’ve got an array of images that I want to randomly attach themselves in some <li> tags. Right now I’ve got everything working great except the images repeat sometimes repeat themselves.

Is there any way to make the images not repeat?

Here’s the code:


// Random Image

var theImages = new Array() 

theImages[0] = 'images/newsalerts/courtroom.jpg"'
theImages[1] = 'images/newsalerts/officer.jpg"'
theImages[2] = 'images/newsalerts/police_dispatcher.jpg"'
theImages[3] = 'images/newsalerts/prison_fence.jpg"'
theImages[4] = 'images/newsalerts/standing_by_car.jpg"'
theImages[5] = 'images/newsalerts/computer_keyboard.jpg"'
theImages[6] = 'images/newsalerts/counselor_inmate.jpg"'
theImages[7] = 'images/newsalerts/crime_scene.jpg"'
theImages[8] = 'images/newsalerts/police_cruiser.jpg"'

var p = theImages.length;
var preBuffer = new Array()
for (i = 0; i < p; i++){
   preBuffer[i] = new Image()
   preBuffer[i].src = theImages[i]
}

//Activate Immediately
$(document).ready(function(){
	
	//Runs Script each time there is an <li>
	$("#news li").each(function(){		
		
		//Generates the Image						
		var whichImage = Math.round(Math.random()*(p-1));
		
		//Puts Image in front of everything in <li>
		$(this).prepend('<img src="'+theImages[whichImage]+'">');
		
	});
	
});

Any help would be appreciated. I hope I have made it clear, If not let me know and I’ll try to explain a little more.

Thanks!

That’s what I mean by a uniform distribution.

I would have thought sorting was inherently biased.

I wasn’t expecting a uniform distribution, I was expecting that by running the test multiple times that I’d get different distributions each time with no obvious bias toward particular arrangements. Instead each test I ran that only sorted once while it gave different results each time did demonstrate a bias toward the elements staying closer to their original position. Changing the comparison reduced the bias.

There is nothing to not like about your solution there since doing an insertion sort that way does guarantee that there should not be any bias in the result.

I had expected that the sort algorithm JavaScript uses was one that would be similarly free of bias since there are a number of algorithms for sorting that do not display any bias however it appears that JavaScript does not use one of those algorithms.

Logically, I wouldn’t expect a uniform distribution!

During a comparison of two elements, you originally set the probability of swapping them to one half. As sorting algorithms are simply a composition of many swaps, the final probability distribution will contain probabilities which are all sums of the reciprocals of powers of two. That makes it impossible to get a uniform distribution for many array lengths.

True, fiddling with your comparison function and shaking the array many times may help, but the final distribution will still be dependent on the sorting algorithm.

The algorithm given below picks each element at random, giving a uniform distribution. What’s not to like?


var shuffled = [];

while (theImages.length) {
	shuffled.push(theImages.splice(Math.random() * theImages.length, 1));
}

Logically with the search algorithm that JavaScript uses I would have expected a completely random distribution.

Since trying to explain how the particular sort algorithm would produce random results is somewhat complicated I decided that running a large test would be an easier way to demonstrate.

JavaScript must not be using the sort algorithm I thought that it was because you are right - the distribution after a single sort isn’t random. The test I did was an array of ten numbers in order zero through nine sorted a million times (starting over with them in order each time). The results did show a bias toward the existing order being maintained with elements staying close to their original position being about twice as likely as moving further away.

Performing multiple sorts one after the other reduced the bias and sorting a table multiple times reduces the bias. Sorting a ten element table fifteen times appears to give fairly random results (I couldn’t see any particular bias across multiple tests when I did that).

If you only have a few items to sort then the bias isn’t going to be all that great and you can reduce it by repeating the sort.

The sort actually showed less bias with subtracting .25 instead of .5 and four sorts using .25 gave me more random results than fifteen sorts using .5. The results after running a million tests with four sorts subtracting .25 gave all the possibilities within a fraction of 1% of each other and rerunning the test suite several times gave similar results with no obvious bias showing up between tests.

So instead of

theImages.sort(function(){return Math.random-.5});

the following will give a far more random result:

theImages.sort(function(){return Math.random-.25});
theImages.sort(function(){return Math.random-.25});
theImages.sort(function(){return Math.random-.25});
theImages.sort(function(){return Math.random-.25});

So creating your own sort code might just possibly be more efficient than running the inbuilt one four times.

What makes you so sure?

While any two elements being compared will be swapped at random, the sort algorithm determines which elements will be compared.

That works GREAT!!! Thank you so much.

If you have a couple of mins do you mind explaining what’s going on here?

I have the basic knowledge of what’s happening but don’t know the specifics.

Thanks again

While that method will shuffle the array, it doesn’t produce a uniform distribution. i.e. some images will be more likely to appear at the start of the array than others.

To shuffle the array, try


var shuffled = [];

while (theImages.length) {
	shuffled.push(theImages.splice(Math.random() * theImages.length, 1));
}

Then assign the src using an index in the jQuery each loop


//Activate Immediately
$(document).ready(function(){
    
    //Runs Script each time there is an <li>
    $("#news li").each(function(index){    
        
        //Puts Image in front of everything in <li>
        $(this).prepend('<img src="'+shuffled[index]+'">');
        
    });
    
});

theImages.sort(function(){return Math.random-.5});

should be completely random in the result. Any bias you get with your results simply means you didn’t take a big enough sample.


// Create an array to hold the shuffled images
var shuffled = [];

// Keep removing one image at random from theImages
// placing it in the shuffled array
// until theImages is empty
while (theImages.length) {
	shuffled.push(theImages.splice(Math.random() * theImages.length, 1));
}

The splice array method is being used to remove and return a single element from theImages.


// remove one image from the start of the array
// i.e. remove the first image
var removed = theImages.splice(0, 1);

// remove one image from index 3 in the array
// i.e. remove the fourth image
var removedToo = theImages.splice(3, 1);

Which element to remove is being chosen at random. The index must be from 0 to one less than the length of the array. The random index generated will be a decimal, but only the integer part is used.


// remove one image from a random position in the array
var randomImage = theImages.splice(Math.random() * theImages.length, 1);

// add the removed image to the shuffled array
shuffled.push(randomImage);

// or remove from original and add to shuffled in one go
shuffled.push(theImages.splice(Math.random() * theImages.length, 1));

As images are removed from theImages array, its length decreases one by one until it reaches zero. At that point, the while condition will be treated as false and the shuffled array will be ready.


while (theImages.length) {
  // will be executed if theImages still has images in it
  // i.e. its length is greater than zero
}

Shuffle the array before you start, and take them in the shuffled order.

theImages[8] = ‘images/newsalerts/police_cruiser.jpg"’;
theImages.sort(function(){return Math.random-.5});

mrhoo - this makes perfect sense but i’m having trouble outputting the images. what do i write to make the images appear?

At this point I have:

var whichImage = Math.round(Math.random()*(p-1));

which now they don’t need to be random anymore?

Any help would be appreciated!

Shuffle the array before you start, and take them in the shuffled order.

theImages[8] = ‘images/newsalerts/police_cruiser.jpg"’;
theImages.sort(function(){return Math.random-.5});

You could create another array, and check against that, but the way I like to do it is with a string containing a letter to represent each image. Take the nth letter, and remove it from the string by replacing with “”