我遇到了一个朴素的随机数生成算法,它产生的一系列数字如下:
for (int i = 0; i < MAX; i++)
if (rand.nextInt(100) >= 100 - probability) // probability is between 0 and 100
randomNumbersList.add(i);
我想知道是否有一种方法可以在不遍历0到MAX之间的每个数字的情况下实现统计上等效的结果。
Binomial b = new Binomial(MAX, p, new MersenneTwister());
int heads = b.nextInt();
TreeSet<Integer> chosen = new TreeSet<>();
for (int i=0, r=0; i<heads; i++) {
do { r = random.nextInt(MAX) } while (chosen.contains(r));
chosen.add(r);
}
p
很高时,这会导致性能非常糟糕,因为内部循环将被执行多次;但是对于这种情况,您的初始算法已经足够好了。p
较低时,所提出的算法将需要与MAX成比例的时间,而不是MAX。这应该可以更好地弥补维护TreeSet
排序的成本。我们将p=probability/100
和q=1-p
。
考虑第一个要添加的数字是什么。以概率q
为0; 以概率(1-q)*q
为1,以概率(1-q)^2*q
为2等等。这就是几何分布。您可以使用以下方法轻松生成按几何分布分布的随机数:生成在[0,1]范围内均匀分布的随机数u
并计算x=⌊ln(u)/ln(q)⌋
- 这个x
将具有几何分布(参见此问题)。
所以这就是如何计算要添加的第一个数字。
现在考虑第二个数字与第一个数字之间的差异。它也将按几何分布分布(仅从1开始,而不是从0开始),因此您可以以相同的方式计算此差异,从而获得第二个数字,依此类推。
伪代码如下:
cur = -1
lnq = ln(q)
while true
u = random(0,1) // float!
cur = cur + 1 + floor(ln(u)/lnq)
if cur >= MAX
break
randomNumbersList.add(cur);
@traveh编写的相应Java代码
List<Integer> randomNumbersList = new LinkedList<Integer>();
int cur = -1;
double p = probability / 100;
double q = 1 - p;
double lnq = Math.log(q);
Random random = new Random();
while (true) {
double u = random.nextDouble();
cur = cur + 1 + (int)Math.floor(Math.log(u) / lnq);
if (cur >= MAX)
break;
randomNumbersList.add(cur);
}
1/ln(q)
。 - tucuxiMAX
次尝试中,你的数字基本上就是成功次数。如果你可以预先确定成功次数,那么你可以从有效范围内获得相同数量的独特随机数。并且它们在统计学上是相同的。所以你要找的是二项式分布。使用成功概率和尝试次数(MAX)从这个分布中获取一个随机数。这将给你随机数的数量。然后获取这么多个随机唯一数字,就完成了。```java```
List<Integer> randomNumbersList = new LinkedList<Integer>();
int cur = -1;
double p = probability / 100;
double q = 1 - p;
double lnq = Math.log(q);
Random random = new Random();
while (true) {
double u = random.nextDouble();
cur = cur + 1 + (int)Math.floor(Math.log(u) / lnq);
if (cur >= MAX)
break;
randomNumbersList.add(cur);
}
probablity
和 100 之间的(可能为空的)“随机”数字列表。其次,您并没有真正迭代0
到MAX
之间的每个数字,代码尝试生成数字MAX
次。如果您想做 X 次某事,必须在某个地方有一个循环。 - Buurmani
添加到列表中。感谢您的纠正。 - Buurman