使用我的Javascript代码进行随机化列表的不同浏览器之间的结果不一致

时间:2012-12-21 06:06:15

标签: javascript sorting cross-browser

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

    $(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中的其他问题中得到解答,但我无法找到这些问题中的“浏览器兼容性”部分。

感谢您的帮助。

3 个答案:

答案 0 :(得分: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 );

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

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

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

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

  

如果comparefn不是该数组元素的一致比较函数,则sort的行为是实现定义的。

     

如果所有值comparefnS和{满足以下所有要求,则函数a是一组值b的一致比较函数集合c中的{1}}(可能是相同的值):符号S表示a <CF b; comparefn(a,b) < 0表示a =CF b(任一标志); comparefn(a,b) = 0表示a >CF b

     

当给定一对特定值comparefn(a,b) > 0comparefn(a,b)作为其两个参数时,调用v始终返回相同的值a。此外,b是数字,Type(v)不是v。请注意,这意味着对于给定的NaNa <CF b对,a =CF ba >CF ba中只有一个为真。

     
      
  • 调用b不会修改此对象。
  •   
  • comparefn(a,b)(反身性)
  •   
  • 如果a =CF a,则a =CF b(对称)
  •   
  • 如果b =CF aa =CF b,则b =CF ca =CF c的及物性传承
  •   
  • 如果=CFa <CF b,则b <CF ca <CF c的及物性传承
  •   
  • 如果<CFa >CF b,则b >CF ca >CF c的及物性传承
  •   
     

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

如果不满足这些属性,优化的排序功能很容易搞砸。这可能意味着根本没有排序,也从不终止排序。见Is it correct to use JavaScript Array.sort() method for shuffling?

相反,使用标准的shuffle算法,有许多好的算法。例如,Fisher-Yates-Shuffle实现起来非常简单,请参阅问题How to randomize (shuffle) a JavaScript array?

答案 1 :(得分:1)

问题是这种情况下的输入。

parseInt(Math.random()* 10)始终返回偶数,因此可以使用随机函数进行折腾。

我在此链接中找到的类似内容。希望它也有帮助。

In Safari and I think also IE7 and 8 Math.random() is NOT random?

答案 2 :(得分:1)

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

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