我从网站上获得了这段代码,用于随机化项目列表。 [这是一个音乐播放器,在用户的每次新访问中随机化歌曲(列表),因此不会因为他们在过去的访问中听到的同一首歌而感到厌烦]
$(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中的其他问题中得到解答,但我无法找到这些问题中的“浏览器兼容性”部分。
感谢您的帮助。
答案 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函数这两个项是相等的。一个好的排序功能会注意到,而不是再问。只有少数项目,您很有可能无法正确排序物品。您可以通过计算(记录)比较函数的调用频率来证明这一点。
此外,您不需要仅返回+1
,0
或-1
,您只需返回任何正数或负数(另请参见下文)。对于随机化,您永远不需要返回相等性。所以,请改用它:
….sort( function() {
return 0.5 - Math.random(); // that would be enough!
} );
但是,根本不应该使用sort
进行随机化。 Math.random
不会导致总排序,并且排序不适用于 :(来自EcmaScript specification)
如果
comparefn
不是该数组元素的一致比较函数,则sort的行为是实现定义的。如果所有值
comparefn
,S
和{满足以下所有要求,则函数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) > 0
和comparefn(a,b)
作为其两个参数时,调用v
始终返回相同的值a
。此外,b
是数字,Type(v)
不是v
。请注意,这意味着对于给定的NaN
和a <CF b
对,a =CF b
,a >CF b
和a
中只有一个为真。
- 调用
b
不会修改此对象。comparefn(a,b)
(反身性)- 如果
a =CF a
,则a =CF b
(对称)- 如果
b =CF a
和a =CF b
,则b =CF c
(a =CF c
的及物性传承- 如果
=CF
和a <CF b
,则b <CF c
(a <CF c
的及物性传承- 如果
<CF
和a >CF b
,则b >CF c
(a >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)。