我正在尝试了解以下任务的解决方案: 从大小为N的数组中随机生成一组M个元素。每个元素的选择概率必须相同。
我找到了以下解决方案(我已经读过this question和this one,但是我仍然有一些问题太长,无法发表评论):
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)。 作者解释如下:
假设我们有一个算法可以从大小为
m
的数组中提取n - 1
个元素的随机集合。我们如何使用该算法从大小为m
的数组中提取n
个元素的随机集合?我们首先可以从前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
元素,因为每个都将以相等的概率被选中。
比我根本不了解的困难部分(递归情况)
M
的数组中生成大小为N - 1
的集合arr[N]
代码由M / N
概率决定添加或不添加“新”元素。随机工作如下:
r
和0
之间生成随机数N
(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个元素。因此,box现在包含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值?
答案 0 :(得分:1)
有choose(n, m) = n! / (m! (n-m)!)
种方法可以从包含m
个元素的集合中选择n
个元素。您想以均等的概率选择这些安排中的任何一种。
关于是否采用给定元素not有两种选择:
m-1
个元素中选择n-1
个元素; m
元素中选择n-1
元素。您必须做出选择,才能产生频率相等的任何安排
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)
为方便起见,引入了A
和B
。
A = choose(n-1, m-1)
= (n-1)! / (m-1)!(n-m)!
B = choose(n-1, m)
= (n-1)! / m!(n-m-1)!
将A
和B
的分子和分母相乘,使它们的公因数为(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
根据需要。