1

我从一个网站获得了这段代码,用于随机化项目列表。[这是一个音乐播放器,在用户每次新访问时随机播放歌曲(列表),因此不会因为从他们在过去访问中听到的同一首歌曲开始而让他们厌烦]

    $(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);            
      });
});

当我在 Chrome 浏览器中打开我的网站时,这工作得很好。排序非常繁重,因此我可以看到该列表在很大程度上是随机的。

但在 Firefox 和 IE 中,排序并没有很好地进行。我看到第一个列表项在 10 次尝试中有 7 次保持在第一位。我可以在许多其他项目中看到相同的情况。例如:第 5 项在 10 次尝试中有 5 次出现在第 3 位。通过这些观察,我可以看出 JS 代码在 IE 和 Firefox 中无法正常工作。(可能是由于引擎不同,不同浏览器对待JS代码的方式不同)

现在,我可以在 JS 代码中更改什么以使其在所有浏览器中都能正常工作吗?

或者是否有任何其他更好的排序算法可以在使用 JS 实现时在所有浏览器中进行不错的排序?

我知道我的第二个问题的一部分已在 SE 中的其他问题中得到解答,但我在这些问题中找不到“浏览器兼容性”部分。

感谢您的帮助。

4

3 回答 3

3

你的随机化不是很好:

// 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 );

这意味着在一半的情况下你 return 0,它告诉排序函数这两个项目是相等的。一个好的排序功能会注意到这一点并且不会再询问。只有少数项目,您很有可能无法很好地分类项目。您可以通过计算(记录)您的比较函数被调用的频率来证明这一点。

此外,您不需要只返回+10或者-1,您可以只返回任何正数或负数(另见下文)。对于随机化,您永远不需要返回相等性。所以,改用这个:

….sort( function() {
     return 0.5 - Math.random(); // that would be enough!
} );

但是,根本不应该sort用于随机化。Math.random不会导致总排序,并且不会为此进行排序:(来自EcmaScript 规范

如果comparefnis […] 不是此数组元素的一致比较函数,则排序的行为是实现定义的。

如果集合中的所有值、和(可能相同的值)都满足以下所有要求,则函数是comparefn一组值的一致比较函数:手段(任一标志);和手段。SabcSa <CF bcomparefn(a,b) < 0a =CF bcomparefn(a,b) = 0a >CF bcomparefn(a,b) > 0

当给定一对特定的值并作为它的两个参数时,调用comparefn(a,b)总是返回相同的值。此外,is Number,而不是。请注意,这意味着 , 和 中的一个对于给定的 和 将是正确的。vabType(v)vNaNa <CF ba =CF ba >CF bab

  • 调用comparefn(a,b)不会修改 this 对象。
  • a =CF a(反身性)
  • 如果a =CF b, 那么b =CF a(对称)
  • 如果a =CF bb =CF c,那么a =CF c( 的传递性=CF)
  • 如果a <CF bb <CF c,那么a <CF c( 的传递性<CF)
  • 如果a >CF bb >CF c,那么a >CF c( 的传递性>CF)

注意:上述条件是必要和充分的,以确保comparefn将集合划分S为等价类并且这些等价类是完全有序的。

如果不满足这些属性,优化的排序功能很容易搞砸。这可能意味着根本没有排序以及永远不会终止排序。请参阅使用 JavaScript Array.sort() 方法进行改组是否正确?

相反,使用标准的洗牌算法,有很多好的。例如,Fisher-Yates-Shuffle 实现起来非常简单,请参阅问题如何随机化(随机播放)JavaScript 数组?.

于 2012-12-21T06:24:03.453 回答
1

在这种情况下,问题是输入。

parseInt( Math.random()*10 ) 一直返回一个偶数,从而使您的随机化函数进行折腾。

我在这个链接中找到了类似的东西。希望它也有帮助。

在 Safari 中,我认为 IE7 和 8 Math.random() 也不是随机的?

于 2012-12-21T06:17:44.977 回答
1

问题是洗牌方法。一个简单但仍然错误的修复方法是将整个排序功能更改为return Math.random() < 0.5 ? 1 : -1;. 但这并不能保证正确随机化列表(一个元素会以 50% 的概率向上移动,而在真正的随机播放中,它会以与最初在其上方的元素数量成比例的概率向上移动)。

正确的做法是实施Fisher-Yates shuffle。这也是 O(n) 而不是 O(n log n)。

于 2012-12-21T06:25:41.547 回答