Fisher-Yates 洗牌算法

leetcode 384. 打乱数组

给你一个整数数组 nums,设计算法来打乱一个没有重复元素的数组。打乱后,数组的所有排列应该是等可能的。

实现 Solution class:
Solution(int[] nums) 使用整数数组 nums 初始化对象
int[] reset() 重设数组到它的初始状态并返回
int[] shuffle() 返回数组随机打乱后的结果

示例 1:
输入
["Solution", "shuffle", "reset", "shuffle"]
[[[1, 2, 3]], [], [], []]
输出
[null, [3, 1, 2], [1, 2, 3], [1, 3, 2]]

解释
Solution solution = new Solution([1, 2, 3]);
solution.shuffle(); // 打乱数组 [1,2,3] 并返回结果。任何 [1,2,3]的排列返回的概率应该相同。例如,返回 [3, 1, 2]
solution.reset(); // 重设数组到它的初始状态 [1, 2, 3] 。返回 [1, 2, 3]
solution.shuffle(); // 随机返回数组 [1, 2, 3] 打乱后的结果。例如,返回 [1, 3, 2]

提示:
1 <= nums.length <= 200
-106 <= nums[i] <= 106
nums 中的所有元素都是 唯一的
最多可以调用 5 * 104 次 reset 和 shuffle

Fisher-Yates 洗牌算法实现

  • 设待原地乱序的数组 nums。
  • 循环 n 次,在第 i 次循环中(0≤i 在 [i,n) 中随机抽取一个下标 j;
     将第 i 个元素与第 j 个元素交换。

证明:打乱后,数组的所有排列是等可能的

长度为n的数组的全排列为n!。故打乱后,每个数组出现的概率均为1/n!。
将a[0]与nums[0, n)后的任意元素交换,交换后的每个数组出现概率为1/n;
将a[1]与nums[1, n)后的任意元素交换,交换后的每个数组出现概率为1/(n-1);
...
将a[n - 1]与nums[n-1, n)后的任意元素交换,交换后的每个数组出现概率为1/1;
故将原数组每个元素与之后的随机元素交换后每个扰乱数组出现的概率为1/n * 1/(n-1) ... * 1/1 = 1/n!。

代码

void shuffle(int a[]) {
    int n = a.size();
    for (int i = 0; i < n; i++) 
        swap(a[i], a[i + rand() % (n - i)]);
    return a;
}

重排列库函数

c++中有库函数random_shuffle随机重排序列:

//generator by default (1):    
template 
  void random_shuffle (RandomAccessIterator first, RandomAccessIterator last);

//specific generator (2):
template 
  void random_shuffle (RandomAccessIterator first, RandomAccessIterator last,
                       RandomNumberGenerator&& gen);

你可能感兴趣的:(leetcode算法)