Skip to content

Latest commit

 

History

History
158 lines (124 loc) · 5.59 KB

528.Random_Pick_with_Weight(Medium).md

File metadata and controls

158 lines (124 loc) · 5.59 KB

528. Random Pick with Weight (Medium)

Date and Time: Nov 30, 2024, 16:26 (EST)

Link: https://leetcode.com/problems/random-pick-with-weight


Question:

You are given a 0-indexed array of positive integers w where w[i] describes the weight of the ith index.

You need to implement the function pickIndex(), which randomly picks an index in the range [0, w.length - 1] (inclusive) and returns it. The probability of picking an index i is w[i] / sum(w).

  • For example, if w = [1, 3], the probability of picking index 0 is 1 / (1 + 3) = 0.25 (i.e., 25%), and the probability of picking index 1 is 3 / (1 + 3) = 0.75 (i.e., 75%).

Example 1:

Input:
["Solution","pickIndex"]
[[[1]],[]]

Output:
[null,0]

Explanation:
Solution solution = new Solution([1]);
solution.pickIndex(); // return 0. The only option is to return 0 since there is only one element in w.

Example 2:

Input:
["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
[[[1,3]],[],[],[],[],[]]

Output:
[null,1,1,1,1,0]

Explanation:

Solution solution = new Solution([1, 3]);
solution.pickIndex(); // return 1. It is returning the second element (index = 1) that has a probability of 3/4.
solution.pickIndex(); // return 1
solution.pickIndex(); // return 1
solution.pickIndex(); // return 1
solution.pickIndex(); // return 0. It is returning the first element (index = 0) that has a probability of 1/4.

Since this is a randomization problem, multiple answers are allowed.
All of the following outputs can be considered correct:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
and so on.

Constraints:

  • 1 <= w.length <= 10^4

  • 1 <= w[i] <= 10^5

  • pickIndex will be called at most $10^4$ times.


Walk-through:

  1. Assign each index from range [0, len(w)-1] a probability from w, and we calculate the probability for each element by self.w[i] / sum(w) (be careful to use the sum of the original w, not the modifying self.w[]).

  2. Then, we calculate the prefixSum by taking self.w[i] += self.w[i-1], for example: [1/8, 3/8, 4/8] will become w = [1/8, 4/8, 1].

  3. Next, we use random.random() to randomly generate a probability in [0, 1], and loop over self.w[] with index and weight to return the index that weight >= prob.


Optimized with Binary Search:

We can optimize the pickIndex() function with Binary Search instead of linear search. We update r = m and also update the res = m, so we can update res to a lower index (closest one).


Prefix Sum & Binary Search Solution:

class Solution:
    # 1. assign each index a probability
    # 2. generate a random probability, find the index that prob >= random_prob

    # TC: O(n), n = len(w), SC: O(n)
    def __init__(self, w: List[int]):
        self.w = w
        total_sum = sum(self.w)
        # Assign prob for each element [0, len(w)-1]
        for i in range(len(w)):
            self.w[i] = self.w[i] / total_sum
        # Add prefixSum for i > 0
            if i > 0:
                self.w[i] += self.w[i-1]

    # TC: O(log n), SC: O(1)
    def pickIndex(self) -> int:
        prob = random.random()
        # Use Binary Search
        l, r = 0, len(self.w)-1
        res = 0
        while l <= r:
            m = (l + r) // 2
            if self.w[m] < prob:
                l = m + 1
            else:
                res = m
                r = m - 1
        return res


# Your Solution object will be instantiated and called as such:
# obj = Solution(w)
# param_1 = obj.pickIndex()

Time Complexity: $O(n + log\ n)$
Space Complexity: $O(n)$


PrefixSum Solution:

class Solution:
    # 1. assign each index a probability
    # 2. generate a random probability, find the index that prob >= random_prob
    # TC: O(n), n = len(w), SC: O(n)

    def __init__(self, w: List[int]):
        self.w = w
        total_sum = sum(self.w)
        # Assign prob for each element [0, len(w)-1]
        for i in range(len(w)):
            self.w[i] = self.w[i] / total_sum
        # Calculate prefix sum for each self.w[i]
        for i in range(1, len(w)):
            self.w[i] += self.w[i-1]

    def pickIndex(self) -> int:
        prob = random.random()
        for i, w in enumerate(self.w):
            if w >= prob:
                return i


# Your Solution object will be instantiated and called as such:
# obj = Solution(w)
# param_1 = obj.pickIndex()

Time Complexity: $O(n)$
Space Complexity: $O(n)$


CC BY-NC-SABY: credit must be given to the creatorNC: Only noncommercial uses of the work are permittedSA: Adaptations must be shared under the same terms