【发布时间】:2016-10-17 09:32:51
【问题描述】:
我想生成在日志空间中均匀分布的随机整数。也就是说, 的值的对数将是均匀分布的。
一个正态均匀分布的 unsigned int 将有 75% 的大小超过 10 亿,大约 99.98% 超过 100 万,因此小值的代表性不足。例如,来自日志空间的统一值将在 4-8 范围内具有与 256-512 相同数量的值。
暂时忽略负值,我能想到的一种方法是:
Random r = new Random();
return (int)Math.pow(2, r.nextDouble() * 31);
这应该会生成一个 31 位日志均匀分布。不过它不会很快,在那里有一个pow() 操作并引入浮点值来生成整数有点难闻。此外,Random.nextDouble() 丢失了double 的很多范围,我不清楚这段代码是否甚至可以生成所有 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 位值,这会产生额外的失真。实际上,范围重叠. 值 1 不仅存在 (p(1)=.5) 移位量为 30,而且移位量为 29 (p(1)=0.25)、28 (p(1)=.125) ,依此类推。对于较小的值(即,如果仅查看 30 和 29 的移位量,1 似乎比 2 的可能性高 3 倍,而不是 2 倍的预测值,但是一旦您查看在更多值时它会收敛。但是,它不会因大值而抵消,这就是为什么您看到 20:32207 存储桶比@sprinter 的答案中的其他存储桶小。
我认为这种形式的偏差可以很容易地通过将最高位强制为零来消除,因此类似于:
(r.nextInt(0x40000000) | 0x40000000) >> r.nextInt(31)
这还有一些其他的调整 - rand 的最大值为 2^30,这更快(nextInt(int) 代码中 2 的幂的特殊情况),因为我们从不想要第二个 MSB 位无论如何设置(我们强制它为1)。这也消除了一个微观的额外偏差来源,即永远无法生成 Integer.MAX_VALUE,因此完整表示中缺少一个值。
它移动 [0,31) 位,因此您永远不会得到零,如果您也想要零,请将其更改为移动 [0,32) 位,您将得到频率等于 1 的零(技术上不记录- 不再分发,但在许多情况下很有用)。另一种方法是从最终值中减去一个以得到零(以永远不会得到 Integer.MAX_VALUE 为代价)。
【问题讨论】:
-
这是您在分布上提出的一个非常好的观点 - 并且通过对我回答中的偏差的解释进行了深思熟虑。也许有一个解决方案可以根据分布中的概率设置每个位。将尝试创建一个 - 尽管我不相信它会比你的更有效。
-
不,抱歉,无法根据每个位被设置的概率找到一种简单的方法来执行此操作。它的数学并不像你知道的那样简单。我认输。
-
我认为如果你的 P(X = x) 与 1/x 成正比,你就会得到这个。但我还没有找到实现这一目标的好方法。
标签: java math random distribution