生成随机浮点数,包括两个边界。

6
我需要生成在区间[-0.5, 0.5]内的随机实数,包括两个边界点
我找到了多种类似的区间生成方法,例如:
-0.5 + Math.random()

但是上限总是 不包括 的,我需要它也包括在内。 0.5 必须在范围内。

4
ејҖеҢәй—ҙ (-0.5; 0.5) жҲ–й—ӯеҢәй—ҙ [-0.5; 0.5]пјҹж•°еӯҰдёҠи®ІпјҢ[-0.5; 0.5] дёҚжҳҜдёҖдёӘеҢәй—ҙпјҢиҖҢжҳҜдёҖз»„ж•°еӯ—гҖӮжҲ‘е°Ҷж¶ҰиүІзҝ»иҜ‘并дҝқжҢҒеҺҹж„ҸпјҢдёҚжҸҗдҫӣд»»дҪ•и§ЈйҮҠжҲ–йўқеӨ–дҝЎжҒҜгҖӮ - Nikolas Charalambidis
2
得到0或1的概率为2/(1.84467440737096e+19)。您确定要求是有意义的吗? - gpunto
@ gpunto 看起来获得1的概率是整数值等于0。 - Antoniossss
4
为什么在精度如此高以至于获得0.5的概率基本为0时,你还在意范围是否包含在内? - marstran
1
你觉得通过除以2来获取下限和上限的概率是否可以接受?如果可以,那么每次随机生成器返回下限时,请返回上限。概率不会在整个范围内均匀分布,但整个范围将包含在过程中。 - Antoniossss
显示剩余3条评论
6个回答

8

实现这个的一种方法是创建一个从-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可获得更低的精度)
这种方法的一个缺点是它没有使用 float/double 数据类型提供的最大精度。

2
你可以通过最小值( 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);

1
双精度浮点数之间的差异并不是恒定的,这是由于双精度数内部的表示方式所导致的。实际上,0.5 + Double.MIN_VALUE > 0.5 的结果为 false。要找到下一个较大的数字,必须在该数字的确切邻域中找出 epsilon 值。 - Peter Walser
利用JVM使用的IEEE浮点表示的知识,我认为你可以通过max = Double.longBitsToDouble(Double.doubleToLongBits(max) + 1L);来获取实际的max+epsilon。 - DodgyCodeException
@DodgyCodeException:很好,结果是相同的最大值+epsilon,已将其添加到答案中,谢谢! - Peter Walser

2
我没有看到任何使用IEEE-754双精度表示内部的位操作的答案,因此这里有一个。
基于观察到滚动到下一个二进制指数与将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开始的值。

0

根据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));

0

@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;

0

Random.nextDouble() 方法返回一个 [0, 1] 范围内的值。因此,如果要将其映射到 [-0.5, 0.5] 范围内,只需减去 0.5。

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


4
实际上,它是 **[0,1)*nextDouble方法的通用契约是生成一个伪随机数,并返回该数作为double类型,该数的范围大致均匀地选自0.0(含)到1.0(不含)之间。* - Antoniossss
3
这段代码基本与原帖中的代码相同。Math.random()实际上调用了nextDouble() - DodgyCodeException

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接