如何在随机生成器中获得50/50的几率

31

我试图在随机生成器中获得1或2的50/50几率。

例如:

Random random = new Random();
int num = random.nextInt(2)+1;

这段代码将输出1或2。

假设我在循环中运行它:

for ( int i = 0; i < 100; i++ ) {
    int num = random.nextInt(2)+1 ;
}

在这种情况下,我该如何使生成器生成相等数量的数字1和2?

所以我希望这个循环生成50次数字1和50次数字2。


9
许多人都注意到,我认为你在这里有些困惑:如果你抛100次硬币,你的期望是50枚正面。介于0到100之间的任何数字都是可能的,但概率不同。我认为你陷入了赌徒谬误的陷阱。 - Ben Allison
1
实际上,你的期望不应该是50个正面,而应该在45到55个正面之间... :D。只是因为50个正面本身最有可能出现(大约有8%的几率)。 - SinisterMJ
5
@AntonRoth,no。 意思是:拒绝AntonRoth提出的某个想法,并附上关于"Expected value"的维基百科链接。 - Rotsor
6个回答

53

一种方法:使用ArrayList<Integer>填充五十个1和五十个2,然后调用Collection.shuffle(...)


1
假设@ Sulaiman确实想要50个1和2,这就是它。但是必须指出实际的随机性已经消失了(可能导致47个1和53个2或类似情况)。 - Mercurybullet
6
@Mercurybullet:我完全同意。像往常一样,准确的解决方案完全取决于他的需求。 - Hovercraft Full Of Eels
为什么生成随机序列这么难? - Mikhail

28

使用 Random.nextBoolean() 很容易实现 50/50 的概率

private final Random random = new Random();

private int next() {
  if (random.nextBoolean()) {
    return 1;
  } else {
    return 2;
  }
}

测试运行:

final ListMultimap<Integer, Integer> histogram = LinkedListMultimap.create(2);
for (int i = 0; i < 10000; i++) {
    nal Integer result = Integer.valueOf(next());
  histogram.put(result, result);
}
for (final Integer key : histogram.keySet()) {
  System.out.println(key + ": " + histogram.get(key).size());
}

结果:

1: 5056
2: 4944

7
据我理解,他需要恰好50个1和50个2。你发布的内容将得到与他已经拥有的相同的结果。 - Simon Arsenault
如果更喜欢三元运算符,可以使用以下代码:private int next() { return random.nextBoolean() ? 1 : 2; } - ziddarth

20

使用random无法实现此目标。如果您需要恰好50个1和50个2,可以尝试类似以下的代码:

int[] array = new int[100];
for (int i = 0; i < 50; ++i)
 array[i] = 1;
for (int i = 50; i < 100; ++i)
 array[i] = 2;

shuffle(array); // implement shuffling algorithm or use an already existing one

4

编辑: 我知道如果你想要完全达到50-50的结果,那么我的答案是不准确的。你应该使用预填充的集合,因为使用任何形式的随机性都不可能实现这一点。考虑到这一点,我的答案仍然适用于问题的标题,所以这就是它:

嗯,你不需要使用随机生成器来做到这一点。 作为来自JavaScript的人,我会选择一行代码:

return Math.random() > 0.5 ? 1: 2;

解释: Math.random() 返回0(含)到1(不含)之间的数字,因此我们只需检查是否大于0.5(中间值)。理论上有50%的机会这样做。

为了更通用的使用,可以将1:2替换为true:false


2
你可以在过程中调整概率,这样得到数字1的概率随着获取数字1的次数而减少。这样你就不会总是有50%的概率得到数字1,但你可以得到你期望的结果(恰好50个数字1):
int onesLeft = 50;

for(int i=0;i<100;i++) {
  int totalLeft = 100 - i;
  // we need a probability of onesLeft out of (totalLeft)
  int r = random.nextInt(totalLeft);
  int num;
  if(r < onesLeft) {
    num = 1;
    onesLeft --;
  } else {
    num = 2;
  }
}

这种方法比洗牌有优势,因为它会递增生成数字,所以不需要内存来存储数字。


1
但这个分布不会是均匀的,你很可能会到达填满相同数字的末尾。 - Danubian Sailor
@lechlukasz,我想过那个问题,但我似乎无法证明或否定。你能吗? - Rotsor
我还有一个证明草图:假设算法对大小为(n-1)的数组进行均匀洗牌,我们可以通过观察第一个选择的数字的概率分布是正确的,并且其余部分是大小为(n-1)的洗牌来将其扩展到大小为n。对于空列表的洗牌基本情况是微不足道的。 - Rotsor
请记住,包含50个零和49个1的序列更有可能以零结尾,因此这可能是您反驳的一个论据。虽然很难确定。 - Rotsor
@Rotsor同意每个分布都有其自身的价值。在这种情况下,我使用“true”表示“公平的50/50概率”,而不是“唯一正确的”。再次为选择不清晰的词语道歉:P - SeKa
显示剩余6条评论

0

您已经成功创建了一个随机生成器,它以相等的概率返回12

正如其他人所提到的,您下一个要求是在100次试验中强制实现精确的50/50分布,这与随机数生成不符。如https://math.stackexchange.com/questions/12348/probability-of-getting-50-heads-from-tossing-a-coin-100-times所示,这种情况发生的现实期望只有约8%。因此,即使您可能希望得到50个每个数字,但这种精确的结果实际上相当罕见。

大数定律表明,随着试验次数的增加,您应该接近预期值。

因此,对于您的实际问题:如何使生成器在这种情况下产生相等数量的1和2?

我能想到的最好(幽默)的答案是:“无限循环运行它。”


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接