关于leetcode算法:FisherYates-洗牌算法

34次阅读

共计 1331 个字符,预计需要花费 4 分钟才能阅读完成。

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<n):
    在 [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 <class RandomAccessIterator>
  void random_shuffle (RandomAccessIterator first, RandomAccessIterator last);

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

正文完
 0