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

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);

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理