【问题标题】:Probability of generating a set of M elements from an array of size N [duplicate]从大小为 N 的数组中生成一组 M 个元素的概率 [重复]
【发布时间】:2018-12-15 23:15:48
【问题描述】:

我正在尝试了解以下任务的解决方案: 从一个大小为 N 的数组中随机生成一组 M 个元素。每个元素被选中的概率必须相等。

我找到了以下解决方案(我已经阅读了this questionthis one,但我仍然有一些问题对于cmets来说太长了):

int rand(int min, int max) { 
  return min + (int)(Math.random() * (max - min + 1));
}

int[] generateSet(int[] arr, int m, int n) {
    if (n + 1 == m) { //base case
        int[] set = new int[m];
        for (int k = 0; k < m; k++) {
            set[k] = arr[k];
        }
        return set;
    }

    int[] set = generateSet(arr, m, n - 1);
    int r = rand(0, n);
    if (r < m) set[r] = arr[n];
    return set;
}
// rand() function returns inclusive value 
// i.e. rand(0, 5) will return from 0 to 5

此代码可在“破解编码面试”一书中找到(困难部分,任务 3)。 作者解释如下:

假设我们有一个算法可以从大小为n - 1 的数组中随机抽取一组m 元素。我们如何使用该算法从大小为n 的数组中提取一组随机的m 元素?我们可以首先从第一个n - 1 元素中拉出一组大小为 m 的随机数。然后,我们只需要决定是否应该将array[n] 插入到我们的子集中(这需要从中提取一个随机元素)。一个简单的方法是从 0 到 n 中选择一个随机数 k。如果k &lt; m,则将array[n] 插入subset[k]。这将“公平地”(即以比例概率)将array[n] 插入子集中并“公平地”从子集中删除一个随机元素。 这甚至更清洁迭代编写。在这种方法中,我们将数组子集初始化为原始中的第一个 m 元素。然后,我们遍历数组,从元素m 开始,将array[i] 插入到(随机)位置k 的子集中,只要k &lt; m

我完全理解基本情况。它说:如果我们有一个大小为NM == N 的数组,因此,我们应该从数组中返回第一个M 元素,因为每个元素都会被选中等概率。

然后是我根本不理解的困难部分(递归案例)。

  1. 代码从大小为N - 1 的数组生成大小为M 的集合
  2. 现在代码应该决定是否将“新”元素 arr[N] 添加到集合中
  3. M / N 代码决定是否添加“新”元素。随机作品如下:

    1. 0N之间生成随机数r
    2. 如果(r &lt; m) 表示r 是用M / N 概率生成的
    3. 另外,如果(r &lt; m) 意味着1 / M 的概率我们将更改集合中的M 个元素之一。

更新:

我不明白以下内容: 想象一下,我们有一个包含 N - 1 个元素的盒子。我们从中提取 M 个元素。因此,得到一组元素的概率为:

Pa(get any set with M elements using N-1 elements) = 1 / ((N-1)! / M!(N-1-M)!) = M!(N-1-M)!) / (N-1)!

很明显,如果我们将所有元素放回盒子中,而不是再次取出 M 个元素,我们将始终创建一个等概率的集合。

好的,假设我们采用 M 个元素。因此,框现在包含N-1-M 元素。

所以这是递归案例的开始: 现在我们从我们的口袋中取出一个作为新元素。现在我们应该决定是否修改集。

从这一点开始,我完全不明白下一步该做什么。我的猜测:

当我们有 N-1 个元素时,生成任何包含 M 个元素的集合的概率为:

Pa(get any set with M elements using N-1 elements) = M!(N-1-M)!) / (N-1)!

但是我们又添加了一个新元素。现在我们应该生成任何 M 个元素的集合,其概率必须等于Pa。 但现在新的概率是:

Pb = 1 / (N! / !M(N-M)!) = M!(N-M)!) / N!

所以我们需要找到一种方法以某种方式将Pb转换Pa,即

!M(N-M)!) / N!!M(N-1-M)!) / (N-1)!

并通过一些魔术(我仍然不明白它是如何工作的)递归案例来做到这一点:

  1. 调用 R = rand(0, X)(我不知道 X 是什么)。如果 R 等于某个 Y(我不知道 Y 值是多少),这意味着我们应该使用我们的新元素。

  2. 如果 R 等于 Y,则调用 rand(0, M) 以生成将使用新元素更新的索引

问题: 1. X和Y值如何计算?

【问题讨论】:

  • 1.它只是脱离了数学。这是我第一次解决的时候,非常令人惊讶,但也很令人满意。
  • @AndyTurner 当然,但究竟是什么数学?我试图推导出表达式,但失败了数十次。
  • 已经有一段时间了。我曾经在这里写过它作为答案,但我认为这个问题已被删除。让我看看能不能再找到它。
  • 我真的很想把这个问题作为以下内容的副本结束:stackoverflow.com/questions/51173120/… 一切都在那里解释。
  • @lexicore 我真的很困惑这个算法和它背后的数学。我已经阅读了您的帖子大约 50 次,但仍然不知道您是如何得出结论的,即如果 Choose(M, N) = N! / (N-M)!Choose(M, N-1) = (N-1)!/(N-1-M)! 那么我们应该决定采用 arr[N] 还是不采用 M/N 概率。我真的需要帮助。也许我可以给你写信给你?

标签: java arrays random set probability


【解决方案1】:

choose(n, m) = n! / (m! (n-m)!) 方法可以从包含n 元素的集合中选择m 元素。您想以相同的概率选择这些安排中的任何一种。

您有两种选择是否将给定元素取为 not:

  1. 选择“this”元素,并从n-1元素中选择m-1元素;
  2. 或不选择“this”元素,而是从n-1 元素中选择m 元素。

您必须以一种可以产生任何频率相同的安排的方式做出选择

P(pick) = (# arrangements which pick "this" element) / (# arrangements)
        = (# arrangements which pick "this" element) / (# arrangements which pick "this" element + # arrangements which do not pick "this" element)
        = A / (A + B)

为了符号方便,引入AB

A = choose(n-1, m-1) 
  = (n-1)! / (m-1)!(n-m)!

B = choose(n-1, m) 
  = (n-1)! / m!(n-m-1)!

AB的分子和分母相乘,使它们的公因数为(n-1)! / m!(n-m)!

A = m     * (n-1)! / m!(n-m)!
B = (n-m) * (n-1)! / m!(n-m)!

然后:

P = m / (m + n - m)
  = m / n

根据需要。

【讨论】:

  • 你所说的“安排”是指设置吗?
  • @NoNameQA 我的意思是一个子集。我使用排列这个词,因为我正在考虑在长度为 n 且值为“1”的二进制字符串中排列 m 位,其中“1”表示应该选择相应的元素。
  • 还有一个问题:你指的是P(pick):“P(pick this element, and pick the m-1 elements from n-1 elements)”?
  • 还有一个问题:我猜对了吗:A = number of subsets which will contain "new" (i.e. arr[N]) elementB = number of subsets which will not contain "new" (i.e. arr[N]) element
  • @NoNameQA “还有一个问题”这是您应该选择选项 1 的概率。
猜你喜欢
  • 2012-11-25
  • 2013-02-24
  • 2014-07-27
  • 1970-01-01
  • 1970-01-01
  • 2020-01-13
  • 1970-01-01
相关资源
最近更新 更多