Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 519. Random Flip Matrix #519

Open
grandyang opened this issue May 30, 2019 · 0 comments
Open

[LeetCode] 519. Random Flip Matrix #519

grandyang opened this issue May 30, 2019 · 0 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

You are given the number of rows n_rows and number of columns n_cols of a 2D binary matrix where all values are initially 0. Write a function flip which chooses a 0 value uniformly at random, changes it to 1, and then returns the position [row.id, col.id] of that value. Also, write a function reset which sets all values back to 0. Try to minimize the number of calls to system's Math.random() and optimize the time and space complexity.

Note:

  1. 1 <= n_rows, n_cols <= 10000
  2. 0 <= row.id < n_rows and 0 <= col.id < n_cols
  3. flip will not be called when the matrix has no 0 values left.
  4. the total number of calls to flip and reset will not exceed 1000.

Example 1:

Input: 
["Solution","flip","flip","flip","flip"]
[[2,3],[],[],[],[]]
Output: [null,[0,1],[1,2],[1,0],[1,1]]

Example 2:

Input: 
["Solution","flip","flip","reset","flip"]
[[1,2],[],[],[],[]]
Output: [null,[0,0],[0,1],null,[0,0]]

Explanation of Input Syntax:

The input is two lists: the subroutines called and their arguments. Solution's constructor has two arguments, n_rows and n_colsflip and resethave no arguments. Arguments are always wrapped with a list, even if there aren't any.

 

这道题让我们随机翻转矩阵中的一个位置,由于之前连续做了好几道随机选点的题 Implement Rand10() Using Rand7()Generate Random Point in a Circle,和 Random Point in Non-overlapping Rectangles。以为这道题也要用拒绝采样 Rejection Sampling 来做,其实不是的。这道题给了一个矩形的长和宽,让每次随机翻转其中的一个点,其中的隐含条件是,之前翻转过的点,下一次不能再翻转回来,而随机生成点是有可能有重复的,一旦很多点都被翻转后,很大概率会重复生成之前的点,所以需要有去重复的操作,而这也是本题的难点所在。博主最先的想法是,既然有可能生成重复的点,那么使用一个 while 循环,只要生成了之前的点,就重新再生成一个,这么一说,感觉又有点像拒绝采样 Rejection Sampling 的原理了。不管了,不管黑猫白猫,能抓耗子🐭的就是好猫🐱。题目中说了尽量减少空间使用度,就不能生成整个二维数组了,可以用一个 HashSet 来记录翻转过了点,这样也方便进行查重操作。所以每次都随机出一个长和宽,然后看这个点是否已经在 HashSe t中了,不在的话,就加入 HashSet,然后返回即可,参见代码如下:

 

解法一:

class Solution {
public:
    Solution(int n_rows, int n_cols) {
        row = n_rows; col = n_cols;
    }
    
    vector<int> flip() {
        while (true) {
            int x = rand() % row, y = rand() % col;
            if (!flipped.count(x * col + y)) {
                flipped.insert(x * col + y);
                return {x, y};
            }
        }
    }
    
    void reset() {
        flipped.clear();
    }

private:
    int row, col;
    unordered_set<int> flipped;
};

 

由于题目中让我们尽量少用 rand() 函数,所以可以进行优化一样,不在同时生成两个随机数,而是只生成一个,然后拆分出长和宽即可,其他部分和上面均相同,参见代码如下:

 

解法二:

class Solution {
public:
    Solution(int n_rows, int n_cols) {
        row = n_rows; col = n_cols;
    }
    
    vector<int> flip() {
        while (true) {
            int val = rand() % (row * col);
            if (!flipped.count(val)) {
                flipped.insert(val);
                return {val / col, val % col};
            }
        }
    }
    
    void reset() {
        flipped.clear();
    }

private:
    int row, col;
    unordered_set<int> flipped;
};

 

其实我们还可以进一步的优化 rand() 的调用数,可以让每个 flip() 函数只调用一次 rand() 函数,这该怎么做呢,这里就有一些 trick 了。需要使用一个变量 size,初始化为矩形的长乘以宽,然后还是只生成一个随机数id,并使用另一个变量 val 来记录它。接下来给 size 自减1,由于 rand() % size 得到的随机数的范围是 [0, size-1],那么假如第一次随机出了 size-1 后,此时 size 自减1之后,下一次不必担心还会随机出 size-1,因为此时的 size 比之前减少了1。如果第一次随机出了0,假设最开始 size=4,那么此时自减1之后,size=3,此时将0映射到3。那么下次如果再次随机出了0,此时 size 自减1之后,size=2,现在0有映射值,所以将 id 改为其映射值3,然后再将0映射到2,这样下次就算再摇出了0,还可以改变id值。大家有没有发现,映射值都是没有没使用过的数字,这也是为啥开始先检测 id 是否被使用了,若已经被使用了,则换成其映射值,然后再更新之前的 id 的映射值,找到下一个未被使用的值即可,参见代码如下:

 

解法三:

class Solution {
public:
    Solution(int n_rows, int n_cols) {
        row = n_rows; col = n_cols;
        size = row * col;
    }
    
    vector<int> flip() {
        int id = rand() % size, val = id;
        --size;
        if (m.count(id)) id = m[id];
        m[val] = m.count(size) ? m[size] : size;
        return {id / col, id % col};
    }
    
    void reset() {
        m.clear();
        size = row * col;
    }

private:
    int row, col, size;
    unordered_map<int, int> m;
};

 

Github 同步地址:

#519

 

参考资料:

https://leetcode.com/problems/random-flip-matrix/

https://leetcode.com/problems/random-flip-matrix/discuss/177809/c%2B%2B-solution

https://leetcode.com/problems/random-flip-matrix/discuss/154053/Java-AC-Solution-call-Least-times-of-Random.nextInt()-function

 

LeetCode All in One 题目讲解汇总(持续更新中...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant