Description
Shuffle a set of numbers without duplicates.
Example:
// Init an array with set 1, 2, and 3.
int[] nums = {1,2,3};
Solution solution = new Solution(nums);
// Shuffle the array [1,2,3] and return its result. Any permutation of [1,2,3] must equally likely to be returned.
solution.shuffle();
// Resets the array back to its original configuration [1,2,3].
solution.reset();
// Returns the random shuffling of array [1,2,3].
solution.shuffle();
思路
分析洗牌算法正确性的准则:产生的结果必须有 n! 种可能,否则就是错误的。这个很好解释,因为一个长度为 n 的数组的全排列就有 n! 种,也就是说打乱结果总共有 n! 种。算法必须能够反映这个事实,才是正确的。
通过这个准则,我发现使用 rand() % nums.size()
实现数组元素的重排是一个错误的想法。假设有个数组为[a, b, c]
,通过这个想法产生的结果有3 * 3 * 3
种,并不是3!
种。因为 27 不能被 6 整除,所以一定有某些情况被“偏袒”了,也就是说某些情况出现的概率会大一些,所以这种打乱结果不算“真的乱”。
通过这篇博客 《洗牌的正确姿势knuth shuffle算法》 认真学习了洗牌算法 Knuth Shuffle,它使得每种排列情况出现的概率相等。
耗时236ms, ranking 25%
class Solution {
public:
Solution(const vector<int> &nums) : nums(nums) {}
/** Resets the array to its original configuration and return it. */
vector<int> reset() {
return nums;
}
/** Returns a random shuffling of the array. */
vector<int> shuffle() {
vector<int> result = nums;
// knuth-shuffle algorithm
for (int i = 1; i < result.size(); ++i) {
int j = rand() % (i + 1);
swap(result[i], result[j]);
}
return result;
}
private:
vector<int> nums;
};
参考
- 《洗牌的正确姿势knuth shuffle算法》:https://yjk94.wordpress.com/2017/03/17/洗牌的正确姿势-knuth-shuffle算法/
- 《洗牌算法》:https://labuladong.gitbook.io/algo/suan-fa-si-wei-xi-lie/xi-pai-suan-fa
- 《[LeetCode] Shuffle an Array 数组洗牌》:https://www.cnblogs.com/grandyang/p/5783392.html