2025-10-10?
初始化 \(a_1 = 1 \dots a_n=n\)
execute the following code:
for i in range(1,n+1):
j = random.randint(1,n)
swap(a[i],a[j])
证明不能等概率得到 \(1\dots n\) 的随机排列
这个题目可以引申很多知识,很有意思。
-
怎么简单修改一下这个代码,使得能等概率随机生成 \(1\dots n\) 的排列:
for i in range(1,n+1): j = random.randint(i,n) swap(a[i],a[j])
为什么?
首先我们发现上面的代码可以生成 \(n!\) 种序列,因为第一个位置可以选 n 个... 第 n 个位置可以选 1 个。
考虑如果选的 \(j_1...j_n\) 和 \(j_1'...j_n'\) 生成了相同的序列:找到最靠前的满足 \(j_k \neq j_k'\) 的 \(k\),那么我们发现进行完 \(1...k-1\) 这些 swap 操作之后,两个操作序列得到的结果相同。进行完
swap(a[k],a[j_k])
和swap(a[k],a[j'_k])
,\(a_k\) 不会再发生任何变化了,不同就是不同了。那么不同的 \(j\) 序列得到的最终结果也会是不同的
-
原题咋做。
考虑有多少种 \(j\) 序列能把 \(a_1..a_n\) 在 swap 之后得到自己。考虑一张图有 \(n\) 个节点,从 i 向 \(j_i\) 连边,得到了一张 \(n\) 个点 \(n\) 条边的有向图。这是一个外向基环树森林。如果它不是由一个个环构成的,那么它一定不能将 \(1..n\) 变成 \(1..n\)
考虑如果这个基环树森林是一些环构成的。考虑在环上的交换行为。在环长大于 3 的时候,每个元素都不能 “换过去再换回来”,也就是走一条长度为 2 的路径回到最开始的位置,而是一定要走一个长度为环长的路径回到原点。
注意到每次连边,会让两个元素的移动距离+1,一个是 \(l^2\),一个是 \(2l\),肯定是不能满足的。而 \(l=2\) 时一定是可以满足的。
此时基环树森林里面只有一元环和二元环,这样的选 \(j\) 的方案是:\(\sum\limits_{k=0}^{n/2} \binom{n}{2k} (2k)!!\)。总方案数 \(n^n\)。此时问题变成了 \(\sum\limits_{k=0}^{n/2} \binom{n}{2k} (2k)!!\neq \dfrac{n^n}{n!}\)
好像这时候直接用右边不是整数就证毕了。这也引出了更直接的一个做法:
如果希望题干代码的运行结果是等概率随机排列,需要满足生成的结果数量是 \(n!\) 的倍数,而 \(n^n\) 并不是。做完了。
不知道 jiayuan 能不能看到这篇博客,也不知道 jiayuan 知不知道什么是外向基环树森林。