Fisher-Yates 洗牌算法 Java 实现
import java.util.Random;
public class FisherYatesShuffle {
public static void shuffle(int[] array) {
Random rand = new Random();
for (int i = array.length - 1; i > 0; i--) {
int j = rand.nextInt(i + 1); // 生成 [0, i] 范围内的随机数
// 交换 array[i] 和 array[j]
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
shuffle(array);
for (int num : array) {
System.out.print(num + " ");
}
}
}
数学证明
Fisher-Yates 洗牌算法通过以下步骤证明其严谨性:
-
第一步:
- 选择一个随机索引 j 在 [0, n-1] 范围内,交换第 n-1 个元素和第 j 个元素。
- 这一步有 n 种可能的选择,每个选择的概率为 1/n。
-
第二步:
- 选择一个随机索引 j 在 [0, n-2] 范围内,交换第 n-2 个元素和第 j 个元素。
- 这一步有 n-1 种可能的选择,每个选择的概率为 1/(n-1)。
-
继续执行:
- 对于第 k 步,选择一个随机索引 j 在 [0, n-k] 范围内,交换第 n-k 个元素和第 j 个元素。
- 这一步有 n-k+1 种可能的选择,每个选择的概率为 1/(n-k+1)。
-
最后一步:
- 选择一个随机索引 j 在 [0, 1] 范围内,交换第 1 个元素和第 j 个元素。
- 这一步有 2 种可能的选择,每个选择的概率为 1/2。
通过组合所有这些步骤,每一个具体的排列 P 出现的概率为:
P(P) = 1/n * 1/(n-1) * 1/(n-2) * ... * 1/2 * 1/1 = 1/n!
由于每一种排列的出现概率都是 1/n!,这就证明了 Fisher-Yates 洗牌算法生成的每一个排列都是等概率的,从而确保了算法的严谨性。