【发布时间】:2018-12-15 23:15:48
【问题描述】:
我正在尝试了解以下任务的解决方案: 从一个大小为 N 的数组中随机生成一组 M 个元素。每个元素被选中的概率必须相等。
我找到了以下解决方案(我已经阅读了this question和this 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 < m,则将array[n]插入subset[k]。这将“公平地”(即以比例概率)将array[n]插入子集中并“公平地”从子集中删除一个随机元素。 这甚至更清洁迭代编写。在这种方法中,我们将数组子集初始化为原始中的第一个m元素。然后,我们遍历数组,从元素m开始,将array[i]插入到(随机)位置k的子集中,只要k < m。
我完全理解基本情况。它说:如果我们有一个大小为N 和M == N 的数组,因此,我们应该从数组中返回第一个M 元素,因为每个元素都会被选中等概率。
然后是我根本不理解的困难部分(递归案例)。
- 代码从大小为
N - 1的数组生成大小为M的集合 - 现在代码应该决定是否将“新”元素
arr[N]添加到集合中 -
M / N代码决定是否添加“新”元素。随机作品如下:- 在
0和N之间生成随机数r - 如果
(r < m)表示r是用M / N概率生成的 - 另外,如果
(r < 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)!
并通过一些魔术(我仍然不明白它是如何工作的)递归案例来做到这一点:
调用 R = rand(0, X)(我不知道 X 是什么)。如果 R 等于某个 Y(我不知道 Y 值是多少),这意味着我们应该使用我们的新元素。
如果 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