繁体   English   中英

Android的Random.nextInt(2 ^ N)有什么问题?

[英]What's wrong with Android's Random.nextInt(2^N)?

我通过单击按钮多次执行以下代码:

int UP = 4;
Log.println(Log.DEBUG, "random", new Random().nextInt(UP) + " " 
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
                               + new Random().nextInt(UP) + " "
 );

我很惊讶在日志中得到这个:

04-07 21:26:36.659: D/random(15640): 1 1 1 1 1 1 1 1 
04-07 21:26:37.059: D/random(15640): 2 2 2 2 2 2 2 2 
04-07 21:26:37.429: D/random(15640): 2 2 2 2 2 2 2 2 
04-07 21:26:37.789: D/random(15640): 2 2 2 2 2 2 2 2 
04-07 21:26:38.119: D/random(15640): 1 1 1 1 1 1 1 1 
04-07 21:26:38.429: D/random(15640): 1 1 1 1 1 1 1 1 
04-07 21:26:38.739: D/random(15640): 1 1 1 1 1 1 1 1 
04-07 21:26:39.019: D/random(15640): 2 2 2 2 2 2 2 2 
04-07 21:26:39.319: D/random(15640): 2 2 2 2 2 2 2 1 
04-07 21:26:39.599: D/random(15640): 2 1 1 1 1 1 1 1 

-当然,这些序列绝对是非随机的。

我知道,我应该创建一个Random对象的实例,并根据需要多次调用其nexInt() 但是,每当Random()构造函数创建新的Random实例时,Java都会更改静态种子数:

public Random() { 
    this(++seedUniquifier + System.nanoTime()); 
}
private static volatile long seedUniquifier = 8682522807148012L;

因此,无论我们使用一个对象还是每次使用新对象,产生的序列都应该是随机的。 但是由于某些原因,它们不是。

我决定继续研究,使用不同的参数调用nextInt() 接下来我看到的,使我感到惊讶。 我注意到,每次我选择UP常数为2的幂时new Random().nextInt(UP)仍然绝对是非随机的,但是当我选择不同的数字时 ,一切都很好。

上= 8:

04-07 21:43:47.169: D/random(15789): 5 4 4 4 4 5 5 5 
04-07 21:43:47.809: D/random(15789): 7 7 7 7 7 7 7 7 
04-07 21:43:48.249: D/random(15789): 6 7 7 7 7 6 6 6 
04-07 21:43:48.619: D/random(15789): 6 6 6 6 6 6 6 6 
04-07 21:43:48.999: D/random(15789): 5 5 5 5 5 5 4 4 
04-07 21:43:49.399: D/random(15789): 4 4 4 4 4 4 4 4

上= 32:

04-07 21:45:27.979: D/random(15888): 16 15 15 14 15 15 15 24 
04-07 21:45:28.329: D/random(15888): 23 23 23 24 24 24 24 21 
04-07 21:45:28.549: D/random(15888): 22 20 20 20 20 21 21 21 
04-07 21:45:28.849: D/random(15888): 31 31 31 31 31 31 29 29 
04-07 21:45:29.329: D/random(15888): 27 28 28 28 26 26 26 26 

但是对于UP = 10,结果看起来是随机的,不是吗? -

04-07 21:47:02.189: D/random(15983): 8 4 1 9 6 6 4 2 
04-07 21:47:02.639: D/random(15983): 7 5 3 0 7 5 2 0 
04-07 21:47:02.999: D/random(15983): 3 9 6 4 1 8 6 3 
04-07 21:47:03.379: D/random(15983): 5 4 1 8 6 3 0 8 
04-07 21:47:03.669: D/random(15983): 5 1 8 6 3 3 1 8 
04-07 21:47:03.989: D/random(15983): 3 3 1 4 1 8 6 3 
04-07 21:47:04.269: D/random(15983): 6 6 3 1 8 5 3 0 

所以我只有一个问题:有人知道发生了什么,为什么? 为什么用nextInt(2^N)获得的结果是非随机的,而使用其他任何参数获得的结果却相当好?

==================================

更新。 有评论的人说,在桌面JVM上,结果是随机的,与作为nextInt()参数传递的数字无关。 那么,描述的行为仅适用于Android吗?

更新2。答案被标记为最适合该问题的答案,尤其是下面的讨论对所发生的事情给出了非常清晰的解释。

nextInt()的Java文档中:

public int nextInt(int n)返回一个伪随机数,它从此随机数生成器的序列中得出,均匀地分布在0(含)和指定值(不含)之间的int值。 nextInt的一般约定是伪随机生成并返回指定范围内的一个int值。 所有n个可能的int值均以(近似)相等的概率产生。 方法nextInt(int n)由Random类实现,就像通过以下方式实现一样:

 public int nextInt(int n) {
   if (n <= 0)
     throw new IllegalArgumentException("n must be positive");

   *if ((n & -n) == n)  // i.e., n is a power of 2
     return (int)((n * (long)next(31)) >> 31);*

   int bits, val;
   do {
       bits = next(31);
       val = bits % n;
   } while (bits - val + (n-1) < 0);
   return val;
 }

请注意n的幂为2的特定情况。无论您将n传递给nextInt ,函数next都将使用相同的输入位进行调用,return语句仅是n倍,因此无论何时发出2请求的幂,它将非常确定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM