如何生成指定区间内的随机浮点数?

42

我想在Java中生成一个随机的浮点数值。该值必须在可能值的特定范围内。

例如,我必须生成一个在以下范围内的随机值:

MIN: 41,815080
MAX: 41,829191

这些值恰好代表地图上可能经度的范围,但问题更普遍适用。

有什么聪明的方法呢?


2
你想要线性分布还是正常分布? - Bohemian
4个回答

88

对于在某个范围内的随机值,公式为:

double random = min + Math.random() * (max - min);

这个基本公式不管你用什么生成随机数的方法都是不变的。 Math.random() 提供了相当分散的数字,但你可以用任何你想要的随机数生成器来替换它,例如(稍微好一些):
Random r = new Random();
double random = min + r.nextDouble() * (max - min);

或者如果你真的想要一个浮动:float
float random = min + r.nextFloat() * (max - min);

为了获得更好的质量(以速度为代价)随机数生成,请使用

Random random = new SecureRandom();

或者您可以使用更加奇特的第三方库,例如:

import org.apache.commons.math3.random.RandomDataGenerator;

RandomData random = new RandomDataGenerator();

使用优秀的Well19937c算法。


1
@truthadjustr 他们怎么样?这段代码适用于所有的maxmin值,无论是正数还是负数。请注意,-1大于-2。 - Bohemian
1
@truthadjustr 不正确。如果min是-3,max是+3,则max-min3 - -3,即3 + 3,即6,常识告诉你这是从-3+3的距离。因此,整个表达式就是-3 + r.nextFloat() * 6。平均而言,产生的值有一半将是负数(介于-3和0之间)。 - Bohemian
1
@MuhammadAshfaq 像所有类C语言一样,min是包含的,max是不包含的。对于浮点数值,“max是不包含的”意味着它几乎可以达到最大值,但并非完全等于最大值,例如,如果最大值为3,则最大值将是2.999999。 - Bohemian
你不能通过这种方式在非常大的范围内生成随机双精度数,如果max很大,而min很小或为零。r.nextDouble() * (Double.MAX_VALUE - 0.0) 只会提供在 1.0e3071.0e308 范围内的随机数,没有更小的数。 - Luke Hutchison
@LukeHutchison 确实。对于所有 |min| + |max| > Double.MAX_VALUE 的情况,它都无法表现良好,随着总和越大,问题会越来越严重。我从未遇到过使用这种范围的情况,也无法想象出这样的情况,但如果编写此函数,您可以检查该范围是否超出此限制,或更简单地要求 min 为非负数,或者只是在 javadoc 中声明此边缘情况未被考虑到。 - Bohemian
显示剩余4条评论

18

如果你想生成随机浮点数,请尝试以下方法:

import java.util.Random;

public static float randFloat(float min, float max) {

    Random rand = new Random();

    return rand.nextFloat() * (max - min) + min;

}
希望这有所帮助。

3
这段代码既无法通过编译,也不能正常工作。您需要返回一个值,并且返回的值应该是 float 类型,而不是 int 类型。 - user4668606

8
我知道这个问题已经超过一年了,但是这里的其他答案遗漏了重要的点。在Java中生成随机浮点数的最佳方法(假设您不需要特定的种子并且不需要密码安全)是使用ThreadLocalRandom类。
例如,如果您想要一个生成两个数字之间的随机数的方法,可以像这样编写:
public static double generateRandomDouble(double min, double max) {
  return ThreadLocalRandom.current().nextDouble(min, max);
}

Math.random的问题在于它会导致竞争。换句话说,如果您的代码与使用Math.random的任何其他代码同时运行,则您的随机数可能需要重新计算。 new Random()的问题在于每次调用它时都会创建一个新的Random实例,这是额外的工作并且更冗长。
这两种方法的问题在于它们强制您进行额外的数学计算以将其缩放到所需范围,从而使您的代码不易读且更容易出错。 ThreadLocalRandom类有一个非常方便的nextDouble重载,允许您指定“原点”和“边界”,即最小值和最大值。
这两种方法的另一个问题是它们不精确。因为您正在进行缩放,所以您需要考虑浮点运算中固有的舍入问题。实际上,本问题的所有其他答案有时都会产生等于max的答案
在上面的代码中,我做了两个假设。首先,您希望下限包含,上限不包含。这个假设相当琐碎,因为实际落在边界上的概率非常小。这也是其他答案试图做到的(尽管它们有时会产生等于max的答案,而我的不会)。我做出的第二个假设是,通过“随机浮点值”,您指的是浮点值,我基于您使用标记该问题的事实做出了这个假设。因此,我提供了一个返回double的方法,这是双精度浮点值,在Java中通常用于浮点值。但是,如果您想指定一个float原始类型,可以使用以下方法:
public static float generateRandomFloat(float min, float max) {
  if (min >= max)
    throw new IllegalArgumentException("max must be greater than min");
  float result = ThreadLocalRandom.current().nextFloat() * (max - min) + min;
  if (result >= max) // correct for rounding
    result = Float.intBitsToFloat(Float.floatToIntBits(max) - 1);
  return result;
}

很遗憾,ThreadLocalRandom上没有nextFloat重载,可能是因为他们预计很少有人需要随机float。要正确编写此方法,必须纠正浮点舍入问题。因此,强烈建议您使用生成double的版本。
总之,您不应该使用Math.random,除非需要特定的种子,也不应该每次需要生成随机数时创建新的Random实例。在生成随机数时,请默认使用ThreadLocalRandom类。

1

代码:

import java.util.Scanner;


public class Hello 
{

    public static void main(String[] args)
    {       

        Scanner sc=new Scanner(System.in);

        double max,min,randfloat;

        System.out.println("Enter minimum :");
        min=sc.nextDouble();
        System.out.println("Enter maximum :");
        max=sc.nextDouble();    

        randfloat=(Math.random())*(max-min)+min; //Math.random() generates random value between 0 and 1

        System.out.println("Random float value generated is: "+randfloat);
        sc.close();

    }

}

Example


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