我遇到了下面的算法,用于在Javascript中对数组进行混洗。它似乎与Fisher-Yates shuffle的区别在于,可用“交换”的范围随着for循环计数器的增加而增加。这似乎与Fisher-Yates版本的行为方式相反。我很好奇这是否是一个有效的算法。这是伪装的费舍尔耶茨吗?有偏见吗?
如果有人能提供一些代码来测试它产生的排列频率,那将是一个奖励。
<script>
var shuffle = function (myArray) {
var random = 0;
var temp = 0;
console.log(myArray);
for (i = 1; i < myArray.length; i++) {
random = Math.round(Math.random() * i);
console.log(random + '\n');
temp = myArray[i];
myArray[i] = myArray[random];
myArray[random] = temp;
console.log(myArray);
}
return myArray;
}
var arr = [1, 2, 3, 4];
shuffle(arr);
</script>
答案 0 :(得分:3)
不,这不是一个公平的洗牌。
Math.random() * i
是0和i
之间的均匀随机浮点值,但Math.round(Math.random() * i)
不会同等地选取0和i
之间的整数。例如,当i
为2时,[0,0.5]范围内的值舍入为0,范围[0.5,1.5]中的值舍入为1,值范围为(1.5,2)这意味着不是同样经常选择0,1和2,而是以0.5的概率选择1,并且每次选取0和2,概率为0.25。
Math.floor(Math.random * (i + 1))
是正确的。
您可以统计验证:将数组[0,1,2]洗牌10000次,看看2在数组末尾保留的频率。应该是3333左右,但由于偏见,它会更像2500.
除此之外,该算法是正确的,可以反过来描述为Fisher-Yates。您可以通过归纳证明它是正确的。 n = 1的基本情况是微不足道的。归纳步骤也相对简单:如果您对n个项目进行了均匀随机排列,然后在0到n + 1的均匀随机索引处插入第n + 1个项目,那么您&# 39;我得到了n + 1项的随机排列。