我目前正在开发一个简单的函数,它会对输入的字符串进行加扰,其中所有可能的排列同样可能。我的代码如下。
function scramble(s) {
result = s.split("");
for(var i = 0; i < s.length; i++) {
var j = Math.floor(Math.random() * (i + 1));
var scrambler = result[i];
result[i] = result[j];
result[j] = scrambler;
}
return result.join("");
}
到目前为止,代码似乎工作正常......但所有可能的排列是否同样可能? (我相信Math.random和Math.floor,但是当我在运行时看到i和j时,我得到了奇怪的输出。)
答案 0 :(得分:4)
除非我在这里犯了一个可怕的错误(请检查它是否有意义),我相信这段代码不会创建统一分布的排列。
检查例如第一个字符将保留的可能性 - 当我为0时,它不能被任何人替换,当我为1时,元素0和1将有50%的交换机会,依此类推。总的来说,结果[0]将保持在概率1/2 * 2/3 * 3/4 ... n-1/n = 1/n
然而,最后一个元素保持原位的概率只是(n-1)/ n,因为你只有一次机会交换它而你从整个数组中选择。
如果将(i+1)
替换为s.length
,您可能会有更好的分布,但最好的选择是采取已知的方法。你可以从这里开始 - http://en.wikipedia.org/wiki/Random_permutation
答案 1 :(得分:0)
深入死灵发布,因为我有一个类似的问题,这是搜索时的第一个结果。我的直觉反应与其他回复相同,但仔细观察后, OPs解决方案似乎是Fisher-Yates的有效变体。
让我们看一下使用OPs实现的示例。
假设S = [1,2,3,4]。
第一次迭代:i === 0,j必须等于0。不进行任何交换。您可以跳过此循环。
第二次迭代:i === 1,j可以为0或1。
[1,2,3,4] (j === 1, no swap so same as original),
[2,1,3,4] (j === 0, swap),
请注意,[0-1]导致所有0.1变化的概率相等
第三次迭代:i === 2,j可以等于0,1,2。
[1,2,3,4], [2,1,3,4] (j === 2, no swap so same as previous results),
[1,3,2,4], [2,3,1,4] (j === 1, swaps),
[3,2,1,4], [3,1,2,4] (j === 0, swaps),
请注意,[0-2]导致所有0,1,2
的均等概率第四次迭代:i === 3,j可以等于0、1、2、3。
[1,2,3,4], [2,1,3,4], [1,3,2,4], [2,3,1,4], [3,2,1,4], [3,1,2,4] (j === 3, no swap so same as previous results),
[1,2,4,3], [2,1,4,3], [1,3,4,2], [2,3,4,1], [3,2,4,1], [3,1,4,2] (j === 2, swaps),
[1,4,3,2], [2,4,3,1], [1,4,2,3], [2,4,1,3], [3,4,1,2], [3,4,2,1] (j === 1, swaps),
[4,2,3,1], [4,1,3,2], [4,3,2,1], [4,3,1,2], [4,2,1,3], [4,1,2,3] (j === 0, swaps),
再次请注意,结果是字符串的所有排列均具有相同的概率!
此工作的原因与链接的Wiki中描述的“现代算法”非常相似。
for i from n−1 down to 1 do
j ← random integer such that 0 ≤ j ≤ i
exchange a[j] and a[i]
Op在每个元素上执行相同的操作(j设置为介于0和i之间),并沿相反的方向循环。每个元素的交换是首先完成还是最后完成都不会改变结果。
OP的实现和Fisher Yates都分享了@leeor发布的推理陷阱。即使元素交换的可能性减小,但每个元素被交换到每个位置的可能性均等,从而为我们提供了理想的结果。