考虑一个问题,其中必须从X中选择k个项目的随机子列表Y,这是n个项目的列表,其中Y中的项目必须以与它们在X中相同的顺序出现.Y中的所选项目不必分明。一个解决方案就是:
for i = 1 to k
A[i] = floor(rand * n) + 1
Y[i] = X[A[i]]
sort Y according to the ordering of A
但是,由于排序操作,它具有运行时间O(k log k)。要删除它,这很诱人
high_index = n
for i = 1 to k
index = floor(rand * high_index) + 1
Y[k - i + 1] = X[index]
high_index = index
但由于统一的索引选择,这给返回列表提供了明显的偏差。如果第二溶液中的指数非均匀分布,则感觉可以获得O(k)溶液。有没有人知道是否是这种情况,如果是这样,边际指数的分布具有哪些属性?
答案 0 :(得分:1)
无偏O(n+k)
解决方案是一个简单的高级伪代码。
histogram[inclusiveRand(1,n)]++
)解释[edit]:
k
n
个i
元素
每个分发,并从中创建histogram。A[i]
,Y
列表中显示的A
次。i
,对于每个元素A[i]
,将Y
插入生成的histogram[i]
列表P(histogram[i]=K) = P(histogram[j]=K)
次。< / LI>
K
,因此对于每个K
,每个元素都有相同的概率出现在结果列表中O(k)
次我相信可以<{1>}使用订单统计信息[X (i)]完成,但我无法理解:\
答案 1 :(得分:1)
通过第一个算法,按排序顺序生成[0,1]的k个均匀随机样本就足够了。
让X1,...,Xk成为这些样本。鉴于Xk = x,X1,...,Xk-1的条件分布是按顺序排列的[0,x]的k-1个均匀随机样本,因此足以对Xk进行采样并递归。
Xk&lt; X? [0,1]的k个独立样本中的每一个必须小于x,因此答案(Xk的累积分布函数)是x ^ k。要根据cdf进行采样,我们所要做的就是在[0,1]的均匀随机样本上反转它:pow(random(), 1.0 / k)
。
这是我实际考虑实施的(预期)O(k)算法。我们的想法是将样本转储到k个bin中,对每个bin进行排序,然后进行连接。这是一些未经测试的Python:
def samples(n, k):
bins = [[] for i in range(k)]
for i in range(k):
x = randrange(n)
bins[(x * k) // n].append(x)
result = []
for bin in bins:
bin.sort()
result.extend(bin)
return result
为什么这种期望有效?假设我们在每个bin上使用插入排序(每个bin的预期大小为O(1)!)。在O(k)的操作之上,我们将按照容器大小的平方和的数量成比例地支付,这基本上是碰撞的数量。由于两个样本碰撞的概率最多为4 / k,我们有O(k ^ 2)个样本对,预期的碰撞数为O(k)。
我非常怀疑O(k)保证可能很有可能。
答案 2 :(得分:0)
您可以使用计数排序对Y进行排序,从而使排序相对于k成线性。但是,为此需要一个长度为n的额外数组。如果我们假设您已经分配了该代码,那么您可以执行您要求的代码,其复杂度为O(k)。
这个想法就像你描述的那样,但我会再使用一个大小为n的数组cnt,我假设它被初始化为0,而另一个我认为是“堆栈”的数据是空的。
for i = 1 to k
A[i] = floor(rand * n) + 1
cnt[A[i]]+=1
if cnt[A[i]] == 1 // Needed to be able to traverse the inserted elements faster
st.push(A[i])
for elem in st
for i = 0 to cnt[elem]
Y.add(X[elem])
for elem in st
cnt[elem] = 0
编辑:正如老人所说,我在帖子中说的不是真的 - 我仍然需要排序st,这可能比原始命题更好但不是太多。因此,如果k与n相当,那么这种方法将是好的,然后我们只是通过cnt线性迭代并以这种方式构造Y.这种方式不需要st:
for i = 1 to k
A[i] = floor(rand * n) + 1
cnt[A[i]]+=1
for i = 1 to k
for j = 0 to cnt[i]
Y.add(X[i])
cnt[i] =0
答案 3 :(得分:0)
对于Y中的第一个索引,X中索引的分布由下式给出:
P(x; n,k)=二项式(n - x + k - 2,k - 1)/ norm
其中二项式表示二项式系数的计算,而norm是一个归一化因子,等于可能的子列表配置的总数。
norm =二项式(n + k - 1,k)
因此,对于k = 5和n = 10,我们有:
我们可以从这个分布中抽取Y中第一个项目的X索引(称之为x1)。然后可以用P(x;(n - x1),(k - 1))以相同的方式对Y中第二个索引的分布进行采样,依此类推所有后续索引。
我现在的感觉是问题在O(k)中无法解决,因为通常我们无法从恒定时间内描述的分布中进行采样。如果k = 2那么我们可以使用二次公式在恒定时间内求解(因为概率函数简化为0.5(x ^ 2 + x))但我看不到将其扩展到所有k的方法(我的数学是'n'但很棒)。
答案 4 :(得分:0)
原始列表X有n个项目。有2个可能的子列表,因为每个项目或将不会出现在结果子列表中:每个项目都会在可能的子列表的枚举中添加一些内容。您可以查看n位的位字的枚举。
由于您只想要具有k个项目的子列表,因此您对设置了k位的位词感兴趣。
一个实用的算法可以从X中挑选(或选择不)第一个元素,然后递归到X的最右边的n-1个子串,同时考虑所选项的累计数量。由于按顺序处理X列表,Y列表也将按顺序排列。
答案 5 :(得分:0)
原始列表X有n个项目。有两个可能的子列表,因为每个项目都会或不会出现在子列表中:每个项目都会在可能的子列表的枚举中添加一些内容。您可以查看n位的位字的枚举。
由于您只想要具有k个项目的子列表,因此您对设置了k位的位词感兴趣。一个实用的算法可以从X中挑选(或选择不是)第一个元素,然后递归到X的最右边的n-1子串中,同时考虑所选项的累积数量。由于按顺序处理X列表,Y列表也将按顺序排列。
#include <stdio.h>
#include <string.h>
unsigned pick_k_from_n(char target[], char src[], unsigned k, unsigned n, unsigned done);
unsigned pick_k_from_n(char target[], char src[]
, unsigned k, unsigned n, unsigned done)
{
unsigned count=0;
if (k>n) return 0;
if (k==0) {
target[done] = 0;
puts(target);
return 1;
}
if (n > 0) {
count += pick_k_from_n(target, src+1, k, n-1, done);
target[done] = *src;
count += pick_k_from_n(target, src+1, k-1, n-1, done+1);
}
return count;
}
int main(int argc, char **argv) {
char result[20];
char *domain = "OmgWtf!";
unsigned cnt ,len, want;
want = 3;
switch (argc) {
default:
case 3:
domain = argv[2];
case 2:
sscanf(argv[1], "%u", &want);
case 1:
break;
}
len = strlen(domain);
cnt = pick_k_from_n(result, domain, want, len, 0);
fprintf(stderr, "Count=%u\n", cnt);
return 0;
}
删除递归是留给读者的练习。 一些输出:
plasser@pisbak:~/hiero/src$ ./a.out 3 ABBA
BBA
ABA
ABA
ABB
Count=4
plasser@pisbak:~/hiero/src$