返回

随机数索引:哈希表预处理与蓄水池抽样

后端

随机数索引:哈希表预处理与蓄水池抽样

哈希表预处理:快速索引查找

假设我们有一个包含整数的数组,我们需要有效地从数组中获取某个特定元素的索引。哈希表预处理 可以轻松解决这个问题。我们可以将数组中的每个元素和它的索引存储在一个哈希表中。这样,当我们想要获取特定元素的索引时,只需查找哈希表即可,时间复杂度为 O(1)。

class Solution {
    private HashMap<Integer, Integer> map;

    public Solution(int[] nums) {
        map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
    }

    public int pick(int target) {
        return map.get(target);
    }
}

蓄水池抽样:概率子集选择

现在,假设我们有一个包含大量元素的集合,我们想要从中随机抽取一个子集。蓄水池抽样 是一种聪明的算法,可以解决这个问题。它的工作原理如下:

  1. 从集合中选择第一个元素并将其放入子集中。
  2. 从第二个元素开始,对于每个元素:
    • 计算它被选入子集的概率(该概率等于子集中的元素数量除以集合中的元素总数量)。
    • 根据概率生成一个随机数,如果随机数小于或等于概率,则将元素加入子集中。

这种方法确保每个元素都有相同的概率被选入子集中。

import java.util.List;
import java.util.Random;

public class ReservoirSampling {

    public static <T> List<T> sample(List<T> elements, int k) {
        List<T> reservoir = new ArrayList<>();

        int count = 0;
        for (T element : elements) {
            count++;

            if (reservoir.size() < k) {
                reservoir.add(element);
            } else {
                int randomIndex = new Random().nextInt(count);
                if (randomIndex < k) {
                    reservoir.set(randomIndex, element);
                }
            }
        }

        return reservoir;
    }
}

选择适合你的方法

哈希表预处理和蓄水池抽样都有其优点和缺点:

方法 优点 缺点
哈希表预处理 快速索引查找 需要额外的空间存储哈希表
蓄水池抽样 无需存储整个集合 每个元素被选中的概率不完全相等

选择最适合你的方法取决于你的特定需求。哈希表预处理对于需要快速索引查找的情况非常有用,而蓄水池抽样对于需要从大集合中选择随机子集的情况非常有用。

常见问题解答

  1. 哈希表预处理的时间复杂度是多少?

    • 预处理:O(n)(n 为数组长度)
    • 索引查找:O(1)
  2. 蓄水池抽样的概率是否总是 100%?

    • 不,对于每一个元素,概率等于子集中的元素数量除以集合中的元素总数量。
  3. 蓄水池抽样是否可以用于从有限流中进行抽样?

    • 是,只要我们能够迭代流中的元素。
  4. 我可以使用哈希表预处理从排序数组中查找索引吗?

    • 可以,但由于数组已经排序,二分搜索会更有效。
  5. 蓄水池抽样和蒙特卡罗抽样有什么区别?

    • 蓄水池抽样保证每个元素都有相同的概率被选入子集,而蒙特卡罗抽样不保证这一点。