问题
如何利用已知random()函数,求一个1~N的全排列?
详细描述
已知:一个random()函数可以生成一个(0,1)范围内的浮点数。
要求:输入N,利用random()函数,生成一个1至N的全排列。
例如:当输入N = 4,生成结果可以为1324或3214等等,并保证等概率。
正文
Step 1
这个题最自然的想法莫过于连续产生随机数,然后如果产生的这个随机数之前没有产生过,就记录并且输出。
这样做显然是能得到结果的,并且能保证足够的随机。
但存在的问题就是会产生些许的浪费,每次产生随机数之后都要确认曾经是否产生过。尤其是对于这样的场景,比如 1 - 3 的全排列,如果已经产生了 3、2,那么我们一定能够确定的是这个全排列的最后一个数一定是 1 ,同样对于倒数第二个数也存在这样的浪费。
Step 2
换种想法,我们可以先做个循环或者其他的方法,产生这么多的数,然后如果能把这些数的顺序随机打乱,也就是一种很好的解法了。
那么问题是如何去随机的打算他们,保证概率呢?
《算法导论》这本书上给过相关的方法,并且有充分的证明。怎么做呢?
伪代码如下:
RANDOMIZE_IN_PLACE (A)
n = A.length
for i=1 to n
swap A[i] with A[Random(i, n)]
那简单的来看如何去理解呢?我们都知道抓阄这个方法是公平的,所以同理,这个方法可以这样理解,有 n 个阄,从 n 个里抓一个,然后剩余 n - 1 个,然后继续从剩下的 n - 1 里面抓。这样保证每个阄都是公平出现的。
这段代码用 JavaScript 实现如下:
A = []
function rd(n,m){
return Math.floor(Math.random() * (m-n+1) + n);
}
for(let i = 0; i < 100; i++) {
A[i] = i + 1;
}
for(let i = 0; i < 100 - 1; i++) {
let index = rd(i+1, 100-1);
A[i] = A[i] ^ A[index]
A[index] = A[index] ^ A[i]
A[i] = A[i] ^ A[index]
}
console.log(A.join("、"))
Step 3
这也就是著名的洗牌算法
版权声明
- 请尊重辛勤劳动
- 禁止不署名完全转载,可单独通过下面的捐赠付版权费
- 建议署名并进行摘要性质转载
捐赠
写文不易,赠我一杯咖啡增强一下感情可好?