我想生成在日志空间中均匀分布的随机整数。也就是说,值的对数将是均匀分布的。
正态均匀分布的无符号整数将有75%的幅度超过10亿,比100万以上99.98%,所以小值不足。来自日志空间的统一值在4-8范围内具有相同数量的值,例如256-512。
暂时忽略负值,我能想到的一种方式是:
Random r = new Random();
return (int)Math.pow(2, r.nextDouble() * 31);
这应该生成一个31位对数均匀分布。它不会很快,在那里进行pow()
操作并引入浮点值来生成整数有点气味。此外,double
遗失了Random.nextDouble()
的很多范围,我不清楚这段代码是否甚至可以生成所有2 ^ 31-1正整数值。
欢迎更好的解决方案。
下面有两个类似的解决方案,它们都涉及用随机位填充整数,然后向右移位一个随机位数。类似的东西:
int number = rand.nextInt(Integer.MAX_VALUE) >> rand.nextInt(Integer.SIZE);
这有两种偏见:
这会产生一种逐步的日志分布值,而不是平滑的值。特别是,在[0,31]中通过随机值右移意味着有31个同样可能的"尺寸"整数,并且该范围内的每个值都是同样可能的。由于范围N中有2 ^ N个值,因此一个范围内的值可能是下一个范围内的值的两倍 - 因此您可以获得范围之间的对数行为,但范围本身是平坦的。
我不知道摆脱这种偏见的简单方法。
出现第二种形式的偏差是因为MSB并不总是1(例如,即使偏移量为10,也不需要产生31-10=21
位值,还会产生额外的失真。范围重叠。对于移位量30,值1不仅存在(p(1)=。5),而且29的移位(p(1)= 0.25),28(p(1)这个效果取消了较小的值(即,如果你只看30和29的移位量,1似乎比2的可能性高3倍,而不是2倍的预测值,但是一旦你看到更多的值它就会收敛。但它并没有取消大值,这就是为什么你看到20:32207
桶比@ sprinter的答案中的其他值小。
我认为这种形式的偏见可以很容易地通过强制顶部位为零来删除,所以类似于:
(r.nextInt(0x40000000) | 0x40000000) >> r.nextInt(31)
这有几个其他调整 - 兰特最多2 ^ 30,这更快(nextInt(int)
代码中2的幂的特殊情况),因为我们从不想要第二个 - 来 - 无论如何MSB位设置(我们强制它为1)。这也消除了微观的额外偏差源,即无法生成Integer.MAX_VALUE,因此完全表示中缺少一个值。
它移位[0,31]位,所以你永远不会得零,如果你也想要零,改变它移位[0,32]位你会得到零的频率等于1(技术上不再进行日志分发,但在许多情况下很有用)。另一种方法是从最终值中减去一个以获得零(以永不获得Integer.MAX_VALUE为代价)。
答案 0 :(得分:1)
提供的错误答案仅供参考。由于问题中给出的原因,这不符合OP的要求。
int number = rand.nextInt(Integer.MAX_VALUE) >> rand.nextInt(Integer.SIZE);
我对此的非正式测试似乎表明存在预期的偏差。我以这种方式生成了1M数字并且具有以下日志分布(忽略零)
0:46819
1:47045
2:40663
3:44001
4:45306
5:43802
6:46447
7:43355
8:47366
9:42747
10:46387
11:43899
12:45179
13:45496
14:44431
15:46751
16:43055
17:47127
18:41243
19:41837
20:32207
21:11965