[LeetCode] Random Pick with Blacklist 帶黑名單的隨機選取


 

Given a blacklist B containing unique integers from [0, N), write a function to return a uniform random integer from [0, N) which is NOT in B.

Optimize it such that it minimizes the call to system’s Math.random().

Note:

  1. 1 <= N <= 1000000000
  2. 0 <= B.length < min(100000, N)
  3. [0, N) does NOT include N. See interval notation.

Example 1:

Input: 
["Solution","pick","pick","pick"]
[[1,[]],[],[],[]] Output: [null,0,0,0] 

Example 2:

Input: 
["Solution","pick","pick","pick"]
[[2,[]],[],[],[]] Output: [null,1,1,1] 

Example 3:

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

Example 4:

Input: 
["Solution","pick","pick","pick"]
[[4,[2]],[],[],[]] Output: [null,1,3,1] 

Explanation of Input Syntax:

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

 

這道題讓我們生成一個N以內的隨機數,但是還給了一個黑名單,意思是黑名單里面的數字不能被選到。於是博主最先想到的方法就是用拒絕采樣Rejection Sampling來做,因為之前做過使用該方法的兩道題 Implement Rand10() Using Rand7() 和 Generate Random Point in a Circle,所以可以立馬想到。思路其實很簡單,就是隨機一個數,如果是黑名單里的,那么就重新隨機。為了提高在黑名單中查找數字的速度,我們將所有黑名單的數字放到一個HashSet中,這樣我們就擁有了常數級查找的速度,看似一切水到渠成,燃鵝被OJ強行打臉,TLE!那么換一種思路吧,既然你有黑名單,那么林北就有白名單,把所有沒被block的數字都放到一個新數組中,然后隨機生成數組坐標不就完了。燃鵝x2,又被OJ放倒了,MLE!不准用這么多內存。豈可修,真的沒別的辦法了嘛?!還好方法解答貼中給了一種使用HashMap的方法來做,博主仔細研讀了一番,發現確實秒啊!既然數字總共有N個,那么減去黑名單中數字的個數,就是最多能隨機出來的個數。比如N=5,黑名單中有兩個數{2, 4},那么我們最多只能隨機出三個,但是我們如果直接rand()%3,會得到0,1,2,我們發現有兩個問題,一是黑名單中的2可以隨機到,二是數字3沒法隨機到。那么我們想,能不能隨機到0或1則返回其本身,而當隨機到2到時候,我們返回的是3,我們需要建立這樣的映射,這就是使用HashMap的動機啦。我們首先將超過N - blacklist.size()的數字放入一個HashSet,這里就把{3, 4}放進去了,然后我們遍歷blacklist中的數字,如果在HashSet中的話,就將其刪除,這樣HashSet中就只有{3}了,這個需要建立映射的數字,而用什么數字建立,當然是用黑名單中的數字了,遍歷黑名單中的數字,如果小於N - blacklist.size()的話,說明是有可能隨機到的,我們和HashSet中的第一個數字建立映射,然后我們可以用個iterator,指向HashSet中的下一個數組,然后繼續建立映射。從而實現在pick函數中的移魂換影大法了,先隨機個數字,如果有映射,則返回映射值,否則返回原數字,參見代碼如下:

 

class Solution {
public:
    Solution(int N, vector<int> blacklist) {
        unordered_set<int> st;
        len = N - blacklist.size();
        for (int i = len; i < N; ++i) st.insert(i);
        for (int num : blacklist) st.erase(num);
        auto it = st.begin();
        for (int num : blacklist) {
            if (num < len) m[num] = *it++;
        }
    }
    
    int pick() {
        int k = rand() % len;
        return m.count(k) ? m[k] : k;
    }

private:
    unordered_map<int, int> m;
    int len;
};

 

類似題目:

Random Pick with Weight

Random Pick Index

 

參考資料:

https://leetcode.com/problems/random-pick-with-blacklist/

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM