【问题标题】:Generate random double from random long从随机长​​生成随机双精度
【发布时间】:2012-05-16 07:52:13
【问题描述】:

在 Java 中,我有一个随机生成器,它生成从 -2^63 到 2^63 的随机数,这不是 java.util.Random。

我需要在 (0,1) 中生成随机双精度,这是我目前所做的:

return (seed/(double)(9223372036854775807L))/2+0.5;//seed is a random long

这是对的吗?有没有数值问题(下溢?)?

可以更好/更快吗?

谢谢。

【问题讨论】:

  • 你可以去掉/2并将其折叠到seed/xxx部分
  • 不,那太长了。
  • 我怎么会这么蠢?谢谢。

标签: java random underflow


【解决方案1】:

我会使用Math.scalb 作为最有效的方法,并确保不会出现由于舍入或表示错误而导致的搞笑行为

double d = Math.scalb(seed >>> 1, -63);

双精度只能使用 53 位,因此有些会被丢弃。

如果你跑了

long seed = Long.MAX_VALUE;
System.out.println(Math.scalb(seed >>> 1, -63));

打印

0.5

种子为 0,你得到 0.0

种子为 -1,你得到 1.0

【讨论】:

  • 这样:double d = Math.scalb(seed >>> 1, 63);返回 d+0.5;我得到这些数字:7.60172909551165E27|2.6062896340692396E35|4.0765687952289006E36
  • 用这个种子 -7545188140698174557 我得到这个数字 1.1819490194320679(大于 1)。
  • 来晚了。 ;) 我一度怀疑 63 是否正确。现在可以试试吗?
【解决方案2】:

我希望只看到一个部门。

0.5+(seed/1.84467440737096E+19);

也就是说,您将遇到浮点精度问题,因为您有 64 个随机整数位,然后您尝试将其压缩为 53 位双精度。为浮点值制作一个专用生成器可能会更好,但我不能肯定地说,因为我不知道你的动机。

【讨论】:

  • 我需要一个大的周期,通常生成器用于随机长,但我也应该实现双随机。
【解决方案3】:

最快的方法可能就是将 long 中的前三位设置为 0,然后使用这些位进行双精度操作。:

double rand = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);

这通过强制符号为正数和指数小于 0 来实现,这将导致尾数至少右移一次。假设 long 中的所有整数都是完全随机的,它给出了一个均匀分布。这是一个完整的 Java 程序,它使用 Random 来生成随机长整数,然后用这个方法将它们转换为 0 到 1 之间的双精度数:

import java.util.Random;

class Main{
    public static void main(String[] args){
        Random rand = new Random();
        long seed = rand.nextLong();
        double x = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
        System.out.println(x);
    }
}

这是 10 次执行的输出:

1.1211565592484309E-247
8.84224349357039E-242
6.956043405745214E-271
3.747746366809532E-232
9.302628573486166E-158
1.1440116527034282E-166
1.2574577719255876E-198
5.104999671234867E-269
3.360619724894072E-213
1.5654452507283312E-220

编辑

这给出了 0 和 1 之间所有可能的双精度数的均匀分布。由于有更多的小双精度数,您可能永远不会看到接近 1 的数字。您可以通过根据现有的,但是您需要一个循环来执行此操作,因此在考虑到这一点后,它可能不是最快的方法:

long exponent = 0;
for(int i = 52; (seed >>> i & 1) > 0; i++) exponent++;
double x = Double.longBitsToDouble(seed & 0x000FFFFFFFFFFFFFL | ((1022 - exponent) << 52));

0.4773960377161338
0.929045618651037
0.7183096209363845
0.33962049395497845
0.45568660174922454
0.11670190555677815
0.09371618427480996
0.8192870898479095
0.9365016017283178
0.11311614413193898

【讨论】:

  • 嗯,我已经删除了评论..现在的数字在范围内并且是随机的..但非常接近于零..我应该再做一些测试! :)
  • @FabioF。啊,这在 0 和 1 之间均匀且随机地分布 所有 个可能的双精度数,但由于存在更多可能的小双精度数,因此在这个意义上它给出了不均匀的分布。
  • 哇,这太棒了,太可怕了..反正我的数字大于 1(1.3484676232953214,这个长 9223048428104072362)。
【解决方案4】:

不完全是。我认为更简单的方法是执行以下操作: new Random(seed).nextDouble()

【讨论】:

  • @FabioF。但你没有说你不能。 ;)
【解决方案5】:

除非我误读了您需要从 0 到 1 的随机双精度数,否则 Java 内置的 Math.random 就是这样做的。因此,您可以避免当前正在进行的所有转换。

【讨论】:

    猜你喜欢
    • 2011-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-10
    • 1970-01-01
    • 2018-11-27
    相关资源
    最近更新 更多