LeetCode #384 Shuffle an Array 数组 洗牌算法

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



参考


原文地址:https://www.cnblogs.com/Bw98blogs/p/12670840.html