我有优化的代码来随机采样包含-1s,0s和1s的数组,概率为1 / 4,1 / 2,1 / 4。它看起来像
#define n (12)
unsigned int x,y=34353,z=57768,w=1564; //PRNG seeds
/* xorshift PRNG
* Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
* Used under CC-By-SA */
u_int32_myRand() {
unsigned int t;
t = x ^ (x << 11);
x = y; y = z; z = w;
return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}
x=(int)time(NULL); //seed PRNG
unsigned int k
int F[n];
for(k=0; k<n; k++) {
F[k]=(1-(myRand()&3))%2;
}
如何修改它以便它只返回其中只有n / 3个零的数组并且仍然快速?
答案 0 :(得分:3)
最简单的方法是用n / 3个零填充数组的第一部分。然后根据需要添加1和-1。然后,执行Fisher-Yates shuffle随机化数组。
尝试“随机分配n / 3个零”的问题是您难以防止重叠。也就是说,如果你想在99的数组中放置33个零,你不能只选择33个随机索引,因为你可能会得到重复。所以你最终会得到阵列中少于33个零。
至于性能,这几乎与您当前的示例一样快。它只需要在阵列上额外传递。生成的随机数的数量是相同的。
答案 1 :(得分:2)
分两步进行:
n/3
个零,并将其余部分设置为1
。示例代码:
int F[n];
// fill with 1
for(k=0; k<n; k++) {
F[k] = 1;
}
// distribute n/3 zeros
for(k=0; k<n/3; k++) {
// find a location which does not have a 0 yet
int i;
do {
i = myRand() % n;
} while(F[i] == 0);
F[i] = 0;
}
// change remaining (non zero) to -1 with 50% probability
for(k=0; k<n; k++) {
if(F[k] && myRand()%2) F[k] = -1;
}
这个运行时间大约为2.4 n,但我认为你不会比这更快。
对于n / 3个零的情况,第二个for循环中的while循环平均执行大约1.2倍。
<强>注:强>
如果成功概率足够高,第二个for
循环中使用的试验和错误效果很好。 p概率所需的平均试验次数为1 / p。
在我们的情况下(n / 3个零),找到好位置(即最后一个零)的最差概率是2/3,因此平均1.5次迭代。要找到所有n / 3个零的位置,平均需要0.2 * n次迭代。
平均运行时间可以计算为-log(1-a)
,其中a
是您要分发的零的百分比(在您的情况下为a = 1/3
)。
更多示例:如果您想要分发2/3 * n个零,则需要1.1 * n次迭代。对于0.99 * n个零,它已经是4.6 * n次迭代。
全部平均。在最糟糕的情况下,它需要永远。
如果您需要运行时保证,通过实现实际采样而不进行重选可能会更好,即填充具有所有可能索引的容器,将随机元素作为索引进行采样并将其从容器中删除。但这可能有大约O(n * log(n))的运行时间。因此,它适用于小n或大百分比的零。
答案 2 :(得分:1)
这是一个简单的算法:
n/3
)放入输出数组1
或-1
概率为1/2
然而:
...包含-1s,0和1的数组,概率为1 / 4,1 / 2,1 / 4
这是否意味着数组中确实存在n/4
1&#39;让我们假设它没有。然后:
a/(a+b)
;在上述算法中使用它而不是1/2
注意:如果您的输入数组中只有零或根本没有零,则无法使用n/3
零进行采样!