Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 什么';安卓系统有什么问题;s Random.nextInt(2^N)?_Java_Android_Random - Fatal编程技术网

Java 什么';安卓系统有什么问题;s Random.nextInt(2^N)?

Java 什么';安卓系统有什么问题;s Random.nextInt(2^N)?,java,android,random,Java,Android,Random,我通过单击按钮多次执行以下代码: int UP = 4; Log.println(Log.DEBUG, "random", new Random().nextInt(UP) + " " + new Random().nextInt(UP) + " " + new Random().nextInt(UP) + " "

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

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()
构造函数创建新的随机实例时,Java都会更改静态种子数:

public Random() { 
    this(++seedUniquifier + System.nanoTime()); 
}
private static volatile long seedUniquifier = 8682522807148012L;
因此,无论我们使用一个对象还是每次使用新对象,生成的序列都应该是随机的。但由于某些原因,它们不是

我决定继续我的研究,用不同的参数调用
nextInt()
。接下来我看到的,让我更加惊讶。我注意到,每次我选择
UP
常量为2的幂时,
new Random()的结果。nextInt(UP)
保持绝对非随机,但当我选择不同的数字时,一切都很正常

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
UP=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,结果看起来是随机的,不是吗--

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

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

更新。评论中的人说,在桌面上,JVM结果是随机的,独立于作为
nextInt()参数传递的数字。那么,所描述的行为是否只针对Android


更新2。这个问题的答案被标记为最佳答案,特别是下面的讨论非常清楚地解释了发生的情况。

来自
nextInt()上的Java文档:

public int nextInt(int n)统一返回伪随机 分布在0(含0)和指定值之间的int值 (排除),从该随机数生成器的序列中提取。这个 nextInt的总合同是指在指定的 范围是伪随机生成并返回的。所有n可能整数 以(近似)相等的概率生成值。方法 nextInt(int n)由Random类实现,如同通过:

public int nextInt(int n){
如果(n>31)*
int位,val;
做{
比特=下一个(31);
val=位%n;
}而(位-val+(n-1)<0);
返回val;
}

注意n是2的幂时的具体情况。不管传递给
nextInt
的n个数是多少,函数
next
都是用相同的输入位调用的,返回语句是n倍,因此,无论何时发出2次幂请求,它都会返回非常确定的结果

“但是Java在Random()构造函数每次创建新的Random实例时都会更改静态种子数目”这似乎与您的数据相矛盾。@LouisWasserman我添加了一段源代码,它表明了这一点……我不知道在nextInt()上放置2^N时为什么会发生这种情况…对于其他数字,一切看起来都很好。如果我没记错的话,Random使用了两个种子。一个是所有实例的常量种子,另一个是启动时的
AtomicLong
,无法在桌面JVM上复制,具有任何
UP
值。@Salauyou您在什么环境下运行此操作?我运行您的代码并对于最多不同的值(包括4,8,32),etcI预期为伪随机分布。etcI尝试使用random()的一个实例并生成nextInt(2^N)很多时候——一切都很好。初始种子值有点问题,它以某种方式与这个“特例”相互作用。对不起,但这个解释到目前为止看起来并不令人信服。如果您查看
next(int位)的实现
您将看到它基于当前种子,而不仅仅是输入。此外,到目前为止,我无法在桌面上重现奇怪的结果。必须尝试使用Android。文档中没有正式的定义,但构造函数中生成的种子值几乎肯定是基于系统时间的(与大多数伪随机生成器一样)。由于您正在快速声明多个构造,因此每个构造的种子值要么具有基本匹配的高阶位,要么完全匹配。除此之外,下一个()函数去掉低阶位,因此如果高阶位匹配,种子将变得相同。因此,next()将返回相同的值,尽管有构造函数语句。@Salauyou嗯,在进行一些调试时,我发现了几件事。1)[非常明显],如果在随机()之间引入一些人工睡眠构造函数你会得到更多的随机结果,这意味着你得到的部分原因是“错误”的种子设定,部分原因是
SeaNick
为2^N.2)Android source for random.java解释的内容(在构造中进行种子设定和
next
)与jdk实现非常不同,android版本之间也有很大差异。例如,在我的4.2.2中,
Random()
构造函数种子的时间以毫秒为单位,而不是以纳秒为单位!!!@SeaNick+1。我同意你的观点,对于2^N,结果更依赖于种子(因为在这种情况下,
next
只被调用一次),而且由于我们在实例之间有非常“紧密”的种子,所以在时间上非常紧密,这使得结果看起来不那么随机。
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 
 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;
 }