【问题标题】:Generate random float, both bounds inclusive生成随机浮点数,包括两个边界
【发布时间】:2019-09-28 03:08:30
【问题描述】:

我需要在[-0.5, 0.5] 范围内生成随机实数,包括两个边界

我找到了各种生成相似范围的方法,例如

-0.5 + Math.random()

但上限始终是独占,我也需要它具有包容性。 0.5 必须在范围内。

【问题讨论】:

  • 打开(-0.5; 0.5) 还是关闭<-0.5; 0.5> 间隔?在数学上,[-0.5; 0.5] 不是区间,而是一组 nuber。
  • 什么精度? 0.10.010.001...?
  • 得到0或1的概率是2/(1.84467440737096e+19)。你确定这个要求有意义吗?
  • 在精度高到0.5的概率基本为0的情况下,为什么还要关心范围是否包含在内?
  • 您是否可以接受除以 2 获得下限和上限的概率? :) 如果是这样,每次随机生成器返回下限时,都返回上限的下限。整个范围的概率不会是统一的,但整个范围都会包含在过程中。

标签: java random


【解决方案1】:

您可以通过比您预期的最大值大的最小值 (epsilon) 来调整上限。要找到 epsilon,请从任何正值开始,并使其尽可能小:

double min = -0.5;
double max = 0.5;

double epsilon = 1;
while (max + epsilon / 2 > max) {
    epsilon /= 2;
}

Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, max + epsilon);

编辑:@DodgyCodeException 建议的替代方案(结果与上述相同):

double min = -0.5;
double max = 0.5;

double maxPlusEpsilon = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L)

Random random = ThreadLocalRandom.current();
DoubleStream randomDoubles = random.doubles(min, maxPlusEpsilon);

【讨论】:

  • 双精度值之间的增量不是恒定的,这是由于双精度数在内部表示的方式。事实上,0.5 + Double.MIN_VALUE > 0.5 就是false。要找到下一个更大的数字,必须找出与数字精确相邻的 epsilon。
  • 使用 JVM 使用的 IEEE 浮点表示的知识,我认为您可以使用 max = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L); 获得实际的 max+epsilon
  • @DodgyCodeException:很好,结果是相同的 max + epsilon,将其添加到答案中,谢谢!
【解决方案2】:

我还没有看到任何在 IEEE-754 Double 表示中使用位摆弄的答案,所以这是一个。

基于观察到下一个二进制指数的翻转与将1 添加到二进制表示中相同(实际上这是设计使然):

Double.longBitsToDouble(0x3ff0000000000000L) // 1.0
Double.longBitsToDouble(0x3ffFFFFFFFFFFFFFL) // 1.9999999999999998
Double.longBitsToDouble(0x4000000000000000L) // 2.0

我想出了这个:

long   l = ThreadLocalRandom.current().nextLong(0x0010000000000001L);
double r = Double.longBitsToDouble(l + 0x3ff0000000000000L) - 1.5;

此技术仅适用于跨越二进制数(1、2、4、8、0.5、0.25 等)的范围,但对于这些范围,此方法可能是最有效和最准确的。此示例针对 1 的跨度进行了调整。对于不跨越二进制范围的范围,您仍然可以使用此技术来获得不同的跨度。应用该技术来获得 [0, 1] 范围内的数字并将结果缩放到所需的跨度。这样的准确率损失可以忽略不计,得到的准确率实际上与Random.nextDouble(double, double)的准确率相同。

对于其他跨度,执行此代码以找到偏移量:

double span = 0.125;

if (!(span > 0.0) || (Double.doubleToLongBits(span) & 0x000FFFFFFFFFFFFFL) != 0)
    throw new IllegalArgumentException("'span' is not a binary number: " + span);
if (span * 2 >= Double.MAX_VALUE)
    throw new IllegalArgumentException("'span' is too large: " + span);

System.out.println("Offset: 0x" + Long.toHexString(Double.doubleToLongBits(span)));

当您将此偏移量插入实际代码的第二行时,您会得到一个范围为 [span, 2*span] 的值。减去span 得到一个从 0 开始的值。

【讨论】:

    【解决方案3】:

    实现此目的的一种方法是创建从 -500 到 500 的随机 int,然后将其除以 1000。

    int max = 500;
    int min = -500;
    int randomInt = rand.nextInt((max - min) + 1) + min;
    float randomNum = randomInt / 1000.00f;
    System.out.println(randomNum);
    

    您可以通过在整数边界和除数中添加和删除零来更改精度。 (例如:创建从 -5 到 +5 的整数并除以 10 以降低精度)

    该解决方案的一个缺点是它不使用浮点/双精度数据类型提供的最大精度。

    【讨论】:

      【解决方案4】:

      @OH GOD SPIDERS' answer 给了我一个想法,将它发展成一个更精确的答案。 nextLong() 在转换为 double 时给出了一个介于 MIN_VALUE 和 MAX_VALUE 之间的值,并且具有足够的精度。

      double randomNum = (rand.nextLong() / 2.0) / Long.MAX_VALUE;
      

      界限是包容性的证明:

      assert (Long.MIN_VALUE/2.0)/Long.MAX_VALUE == -0.5;
      assert (Long.MAX_VALUE/2.0)/Long.MAX_VALUE == 0.5;
      

      【讨论】:

        【解决方案5】:

        鉴于 HOW GOD SPIDERS 的回答,这里有一个现成的功能:

        public static double randomFloat(double minInclusive, double maxInclusive, double precision) {
            int max = (int)(maxInclusive/precision);
            int min = (int)(minInclusive/precision);
            Random rand = new Random();
            int randomInt = rand.nextInt((max - min) + 1) + min;
            double randomNum = randomInt * precision;
            return randomNum;
        }
        

        然后

        System.out.print(randomFloat(-0.5, 0.5, 0.01));
        

        【讨论】:

          【解决方案6】:

          Random.nextDouble 给出 [0, 1] 范围内的值。因此,要将其映射到 [-0.5, 0.5] 的范围,您只需减去 0.5。

          您可以使用此代码获得所需的输出 double value = r.nextDouble() - 0.5;

          【讨论】:

          • 其实是[0,1)nextDouble的一般约定是一个double值,从0.0d(包括)到 1.0d(不包括),是伪随机生成并返回的。
          • 这段代码和OP的代码基本一致。 Math.random() 实际上调用了nextDouble()
          猜你喜欢
          • 2011-07-14
          • 2010-10-15
          • 1970-01-01
          • 2023-04-01
          • 2023-01-03
          • 2021-11-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多