Java:0 <= x <n范围内的随机长整数


135

随机类具有一种在给定范围内生成随机int的方法。例如:

Random r = new Random(); 
int x = r.nextInt(100);

这将生成一个大于或等于0且小于100的int数。我想对long数做完全相同的操作。

long y = magicRandomLongGenerator(100);

随机类仅具有nextLong(),但不允许设置范围。


相关,可能是有用的:stackoverflow.com/questions/2290057/...
TJ克罗德

1
您是否考虑过只是获得长时间随机数并采用范围的mod?(当然,如果范围只有100,我会产生一个int随机数并将其
强制转换

java.util.Random仅使用48位分配(请参见实现详细信息),因此不会具有正态分配。
Geoffrey De Smet 2012年

1
在现代,可以考虑使用org.apache.commons.lang3.RandomUtils#nextLong。
realnice

Answers:


149

Java 7(或Android API级别21 = 5.0+)开始,您可以直接使用ThreadLocalRandom.current().nextLong(n)(对于0≤x <n)和ThreadLocalRandom.current().nextLong(m, n)(对于m≤x <n)。有关详细信息,请参见@Alex的答案。


如果您坚持使用Java 6(或Android 4.x),则需要使用外部库(例如org.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1),参见@mawaldne的答案),或实现自己的库nextLong(n)

根据https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.html nextInt实现为

 public int nextInt(int n) {
     if (n<=0)
                throw new IllegalArgumentException("n must be positive");

     if ((n & -n) == n)  // i.e., n is a power of 2
         return (int)((n * (long)next(31)) >> 31);

     int bits, val;
     do {
         bits = next(31);
         val = bits % n;
     } while(bits - val + (n-1) < 0);
     return val;
 }

所以我们可以修改它来执行nextLong

long nextLong(Random rng, long n) {
   // error checking and 2^x checking removed for simplicity.
   long bits, val;
   do {
      bits = (rng.nextLong() << 1) >>> 1;
      val = bits % n;
   } while (bits-val+(n-1) < 0L);
   return val;
}

1
我在“ 2 ^ x检查”部分遇到了一些问题。有任何想法吗?
Vilius Normantas

@Vilius:2 ^ x检查只会使生成速度更快,因为直接使用rng.nextLong() % n会得到统一的值(假设所有位都很好)。您可以根据需要忽略该部分。
kennytm 2010年

如果需要m <= x <= n,您将如何修改解决方案?
BJ Peter DeLaCruz 2014年

6
@BJPeterDeLaCruz:A之间的随机数mn可以与之间的随机数来获得0n-m,再加入m
kennytm

84

ThreadLocalRandom

ThreadLocalRandom有一种nextLong(long bound)方法。

long v = ThreadLocalRandom.current().nextLong(100);

nextLong(long origin, long bound)如果您需要非0的原点,也可以使用。传递原点(包括)和界限(不包括)。

long v = ThreadLocalRandom.current().nextLong(10,100); // For 2-digit integers, 10-99 inclusive.

SplittableRandom具有相同的nextLong方法,如果您想要可重复的数字序列,则可以选择种子。


5
这个答案比投票最多的答案更简单,因此更有用。
yurin 2015年

2
对于那些为Android开发,请注意这是只能从API 21(棒棒堂,安卓5.0):developer.android.com/reference/java/util/concurrent/...
Android开发者

75

生成范围内数字的标准方法(无实用方法)是对范围使用双精度数:

long range = 1234567L;
Random r = new Random()
long number = (long)(r.nextDouble()*range);

将为您提供介于0(含)和范围(不含)之间的较长时间。同样,如果您想要一个介于x和y之间的数字:

long x = 1234567L;
long y = 23456789L;
Random r = new Random()
long number = x+((long)(r.nextDouble()*(y-x)));

将为您提供从1234567(含)到123456789(不含)的较长时间

注意:检查括号,因为强制转换为long的优先级高于乘法。


5
我的第一个想法就是这个。但这似乎有点不雅。而且我担心分布的均匀性(不是我真的需要它,我只是想做正确的事)
Vilius Normantas 2010年

6
请不要使用它。输出完全不一致。
纳文

2
最大的问题是舍入将使最低位非常不均匀。而且,bound必须小于可以以双精度2 ^ 53编码的最大整数。
Aleksandr Dubinsky

12

上面的方法效果很好。如果您使用的是Apache Commons(org.apache.commons.math.random),请查看RandomData。它有一个方法:nextLong(long lower,long upper)

http://commons.apache.org/math/userguide/random.html

http://commons.apache.org/math/api-1.1/org/apache/commons/math/random/RandomData.html#nextLong(long,%20long)


3
为了后代:在4.0中不推荐使用RandomData。使用commons.apache.org/proper/commons-math/apidocs/org/apache/…–
Michael Tontchev

11

使用'%'运算符

resultingNumber = (r.nextLong() % (maximum - minimum)) + minimum;

通过使用'%'运算符,我们将余数除以您的最大值。这样我们只剩下从0(含)到除数(不含)的数字。

例如:

public long randLong(long min, long max) {
    return (new java.util.Random().nextLong() % (max - min)) + min;
}

很好,但是您应该检查if (max == min)
khcpietro 2014年

并检查if (nextLong() >= 0)
khcpietro 2014年

6
仅供参考:这并不总是能提供均匀的分布,对于某些较大范围的情况确实很不利。例如,如果min = 0max = 2 * (MAX_LONG / 3),那么你很可能会作为两次得到一个值[0, MAX_LONG / 3],你是让中的一个[MAX_LONG / 3, 2 * (MAX_LONG / 3)]
尼克

此代码无效。如果nextLong返回负值,则余数将为负,并且该值将超出范围。
Arnaud 2015年

3

进一步改善kennytm的答案:考虑Java 8中的实际实现的子类实现将是:

public class MyRandom extends Random {
  public long nextLong(long bound) {
    if (bound <= 0) {
      throw new IllegalArgumentException("bound must be positive");
    }

    long r = nextLong() & Long.MAX_VALUE;
    long m = bound - 1L;
    if ((bound & m) == 0) { // i.e., bound is a power of 2
      r = (bound * r) >> (Long.SIZE - 1);
    } else {
      for (long u = r; u - (r = u % bound) + m < 0L; u = nextLong() & Long.MAX_VALUE);
    }
    return r;
  }
}

我知道这是一个古老的答案,不太可能使用,但是这部分显然是错误的:if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } 首先,很容易通过单元测试证明它实际上并不会产生范围为[0,bound]的数字。其次,它不必要地详尽说明:r = r & m可以实现所需的结果,而这基本上就是当前Java 8实现所要做的。编写此答案时,实现方式可能会有所不同,但未必一定如此。
E. Bishop

3

如果要在[0,m)范围内长时间均匀分布的伪随机数,请尝试将模运算符和绝对值方法与方法结合使用,nextLong()如下所示:

Math.abs(rand.nextLong()) % m;

哪里 rand您的随机对象。

模运算符将两个数相除,然后输出这些数的余数。例如,3 % 21由于在3和2的余数是1。

由于nextLong()生成的均匀分布的伪随机数的长度长于[-(2 ^ 48),2 ^ 48)(或该范围内的某个范围),因此您需要取其绝对值。如果不这样做,则该nextLong()方法的模数有50%的机会返回负值,该值超出[0,m)。

您最初要求的是均匀分布的伪随机数,其范围为[0,100]。以下代码这样做:

Math.abs(rand.nextLong()) % 100;

1
模数有偏差,请勿将其用于随机stackoverflow.com/a/10984975/1166266
Sirens

2

这个怎么样:

public static long nextLong(@NonNull Random r, long min, long max) {
    if (min > max)
        throw new IllegalArgumentException("min>max");
    if (min == max)
        return min;
    long n = r.nextLong();
    //abs (use instead of Math.abs, which might return min value) :
    n = n == Long.MIN_VALUE ? 0 : n < 0 ? -n : n;
    //limit to range:
    n = n % (max - min);
    return min + n;
}


很好,除了属于框架的部分(我猜)。
Damir Olejar

2

下面的方法将返回10000000000到9999999999之间的一个值

long min = 1000000000L
long max = 9999999999L    

public static long getRandomNumber(long min, long max){

    Random random = new Random();         
    return random.nextLong() % (max - min) + max;

}

当我重置long min = 1L时;最大最大= 10升; 产生的随机数超出最大值!
Raj Rajen

它应该是random.nextLong()%(max-min)+ min;
杰伊·乔迪瓦

2

Java 8 API

API doc https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long进行实际实现可能会更容易, 他们正在使用它来产生长流。就像问题中一样,您的来源可以是“ 0”。

long nextLong(long origin, long bound) {
  long r = nextLong();
  long n = bound - origin, m = n - 1;
  if ((n & m) == 0L)  // power of two
    r = (r & m) + origin;
  else if (n > 0L) {  // reject over-represented candidates
    for (long u = r >>> 1;            // ensure nonnegative
         u + m - (r = u % n) < 0L;    // rejection check
         u = nextLong() >>> 1) // retry
        ;
    r += origin;
  }
  else {              // range not representable as long
    while (r < origin || r >= bound)
      r = nextLong();
  }
  return r;
}

1

随机页面上:

nextLong方法由Random类实现,就像通过以下方式实现一样:

public long nextLong() {
   return ((long)next(32) << 32) + next(32);
}

因为类Random使用仅48位的种子,所以此算法将不会返回所有可能的长值。

因此,如果您想获得一个Long,您将不会获得完整的64位范围。

我建议,如果您的范围接近2的幂,则可以建立 Long可以在该代码段中建立,如下所示:

next(32) + ((long)nextInt(8) << 3)

例如,获得35位范围。


2
但是文档说“所有2 ^ 64个可能的长值都是(近似)相等的概率产生的”。因此,显然,nextLong()方法应返回所有可能的值。顺便说一句,种子的长度与值的分布如何相关?
Vilius Normantas,2010年

0

使用的方法r.nextDouble()应使用:

long number = (long) (rand.nextDouble()*max);


long number = x+(((long)r.nextDouble())*(y-x));

0
public static long randomLong(long min, long max)
{
    try
    {
        Random  random  = new Random();
        long    result  = min + (long) (random.nextDouble() * (max - min));
        return  result;
    }
    catch (Throwable t) {t.printStackTrace();}
    return 0L;
}

1
您不应该Random临时创建实例,不需要时不要捕获Throwables或其他异常,应该使用某种日志记录框架(例如SLF4J)记录错误,而不要使用printStackTrace
虚假

0

如果可以使用Java流,则可以尝试以下操作:

Random randomizeTimestamp = new Random();
Long min = ZonedDateTime.parse("2018-01-01T00:00:00.000Z").toInstant().toEpochMilli();
Long max = ZonedDateTime.parse("2019-01-01T00:00:00.000Z").toInstant().toEpochMilli();
randomizeTimestamp.longs(generatedEventListSize, min, max).forEach(timestamp -> {
  System.out.println(timestamp);
});

这将长时间生成给定范围内的数字。


0
import java.util*;

    Random rnd = new Random ();
    long name = Math.abs(rnd.nextLong());

这应该工作


-4

//使用系统时间作为种子值以获得一个好的随机数

   Random random = new Random(System.currentTimeMillis());
              long x;
             do{
                x=random.nextLong();
             }while(x<0 && x > n); 

//循环直到得到一个大于或等于0且小于n的数字


1
这可能是极端无效的。如果n是1或说2,该怎么办?该循环将执行许多迭代。
Magnilex
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.