看了网上其他同学给出的算法及其证明,但没有看懂证明过程,遂自己思考后写了本文,主要是为了证明算法的正确性。
问题描述
大小为n的数组,要求随机打乱数组元素顺序,并且使得任意元素在任意位置的概率相同,即为 1n 。
算法
本文直接给出比较流行的时间复杂度为O(n),空间复杂度为O(1)算法。
void Shuffle(int *array,int size)
{
for(int i=1;i<size;i++)
{
int index = Rand(0,i);
std::swap(array[i],array[index]);
}
}
证明
定理:根据上述算法,任意元素放入任意位置的概率为 1n 。
分析:使用数学归纳法进行证明,我只给出前3个元素的分析过程。
1.当n=1时,一个元素放在一个位置的概率为1
2.当n=2时,根据算法,第一个位置已经暂且定下位置。第二个元素只能随机两个数字,概率均为 12 ,第一个元素要么和第二个元素置换,要么位置不变,两个元素在任意这两个位置的概率都是 12 。
3.当n=3时,根据算法,前两个元素已经暂且定下位置。我们先看看第3个元素在任意位置的概率是多少?就是 13 ,因为随机出来的3个数字的概率相同。在没有第3个元素和第3个位置时,前两个元素的任意位置概率是 12 ,那现在有3个位置,自然概率就要变化:假设第一个位置的元素值是10,那么这个10现在只有两种情况,要么保持位置不变,要么和第3个位置交换。
1)保持位置不变的概率是10在前两个位置的概率乘以新随机的数不是该位置的概率,即 12×(1−13) ,结果是 13 。
2)与第3个位置进行交换的概率是10在第一个位置的概率乘以随机出第一个位置数的概率加上10在第二个位置的概率乘以随机出第二个位置数的概率,即 12×13+12×13 ,结果为 13 。
综上,第一个位置的随机到任意位置的概率为 13 。第二个位置也可以这么证明,所以干脆下面证明时直接说是任意一位置。
证明: 基础步骤: 当n=1时,第一位置元素只能随机出1,概率为1,定理成立。
归纳步骤: 当n=k,k个元素在任k个位置的概率均为 1k 成立。
当n=k+1,第k+1位置的元素在任k+1个位置的概率是 1k+1 ,因为随机出k+1个数概率是一样的。前k个元素的概率发生了变化,任意一元素保持位置不变的概率为 1k×(1−1k+1) ,结果为 1k+1 ;任意一元素与第k+1元素进行置换的概率为 ∑k1(1k×1k+1) ,结果为 1k+1 ,综上,前k个元素在任意位置的概率为 1k+1 。
定理成立,证毕。