Reputation: 1416
I got this piece of code from a website for randomizing a list of items. [This is for a music player that randomizes the songs (list) in every new visit by the user, thus not boring them by starting with the same song that they heard in the past visit]
$(document).ready(function(){
$('ul').each(function(){
// get current ul
var $ul = $(this);
// get array of list items in current ul
var $liArr = $ul.children('li');
// sort array of list items in current ul randomly
$liArr.sort(function(a,b){
// Get a random number between 0 and 10
var temp = parseInt( Math.random()*10 );
// Get 1 or 0, whether temp is odd or even
var isOddOrEven = temp%2;
// Get +1 or -1, whether temp greater or smaller than 5
var isPosOrNeg = temp>5 ? 1 : -1;
// Return -1, 0, or +1
return( isOddOrEven*isPosOrNeg );
})
// append list items to ul
.appendTo($ul);
});
});
This works perfectly fine when I open my site in Chrome browser. The sorting is done pretty heavily and hence I could see that the list is randomized to a great extent.
But in Firefox and IE, the sorting is not happening to a good extent. I see that the first list item remains first in 7 out of 10 tries. And I could see the same for many other items. Ex: Item #5 occur in 3rd position in 5 out of 10 tries. With these observations, I could tell that the JS code is not working properly in IE and Firefox. (may be due to the way different browsers treat JS code because of the differences in the engine)
Now, Is there anything I can change in the JS code to make it work in all browsers?
Or Is there any other better sorting algorithm that would do a decent sorting in all browsers when implemented using JS?
I understand that a part of my 2nd question has been answered in other questions within SE but I couldn't find about the 'browser compatibility' part in those questions.
Thanks for helping.
Upvotes: 1
Views: 1349
Reputation: 665020
Your randomisation is not very good:
// Get a random number between 0 and 10
var temp = parseInt( Math.random()*10 );
// Get 1 or 0, whether temp is odd or even
var isOddOrEven = temp%2;
// Get +1 or -1, whether temp greater or smaller than 5
var isPosOrNeg = temp>5 ? 1 : -1;
// Return -1, 0, or +1
return( isOddOrEven*isPosOrNeg );
That means in a half of the cases you return 0
, which tells the sort function that the two items are equal. A good sort function will notice that and not ask again. With only a few items, you have a very high chance of the items not getting sorted well. You can proof this by counting (logging) how often your compare-function is called.
Also you don't need to return only +1
, 0
or -1
, you can just return any positive or negative number (see also below). For randomizing, you never really need to return equality. So, use this instead:
….sort( function() {
return 0.5 - Math.random(); // that would be enough!
} );
However, one should not use sort
for randomisation at all. Math.random
does not lead to a total ordering, and sort is not made for that: (from the EcmaScript specification)
If
comparefn
is […] not a consistent comparison function for the elements of this array, the behaviour of sort is implementation-defined.A function
comparefn
is a consistent comparison function for a set of valuesS
if all of the requirements below are met for all valuesa
,b
, andc
(possibly the same value) in the setS
: The notationa <CF b
meanscomparefn(a,b) < 0
;a =CF b
meanscomparefn(a,b) = 0
(of either sign); anda >CF b
meanscomparefn(a,b) > 0
.Calling
comparefn(a,b)
always returns the same valuev
when given a specific pair of valuesa
andb
as its two arguments. Furthermore,Type(v)
is Number, andv
is notNaN
. Note that this implies that exactly one ofa <CF b
,a =CF b
, anda >CF b
will be true for a given pair ofa
andb
.
- Calling
comparefn(a,b)
does not modify the this object.a =CF a
(reflexivity)- If
a =CF b
, thenb =CF a
(symmetry)- If
a =CF b
andb =CF c
, thena =CF c
(transitivity of=CF
)- If
a <CF b
andb <CF c
, thena <CF c
(transitivity of<CF
)- If
a >CF b
andb >CF c
, thena >CF c
(transitivity of>CF
)NOTE: The above conditions are necessary and sufficient to ensure that
comparefn
divides the setS
into equivalence classes and that these equivalence classes are totally ordered.
An optimised sort function can easily screw up if these properties are not fulfilled. That can mean no sorting at all as well as never terminating sorting. See Is it correct to use JavaScript Array.sort() method for shuffling?
Instead, use a standard shuffle algorithm, there are many good ones. The Fisher-Yates-Shuffle is really simple to implement for example, see the question How to randomize (shuffle) a JavaScript array?.
Upvotes: 3
Reputation: 1026
The problem is the shuffling method. An easy but still wrong way to fix it is change the whole sort function to return Math.random() < 0.5 ? 1 : -1;
. But that isn't guaranteed to randomize the list properly (an element will move up with a probability of 50%, whereas in a true shuffle it will move up with a probability proportional to the number of elements originally above it).
The correct thing to do is implement a Fisher-Yates shuffle. This is also O(n) rather than O(n log n).
Upvotes: 1
Reputation: 2883
The problem is the input in this case.
parseInt( Math.random()*10 ) returns an even number all the time, thus taking your randomising function for a toss.
Something similar I found in this link. Hope it also helps.
In Safari and I think also IE7 and 8 Math.random() is NOT random?
Upvotes: 1