Java: случайное длинное число в диапазоне 0 <= x <n

135

Случайный класс имеет метод для генерации случайных int в заданном диапазоне. Например:

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

Это сгенерирует целое число больше или равное 0 и меньше 100. Я хотел бы сделать то же самое с длинным числом.

long y = magicRandomLongGenerator(100);

Случайный класс имеет только nextLong (), но он не позволяет устанавливать диапазон.

Вилюс Нормантас
источник
Связано, может быть полезно: stackoverflow.com/questions/2290057/…
TJ Crowder
1
Рассматривали ли вы просто получить свою длинную случайную комбинацию и использовать мод своего диапазона? (Конечно, если диапазон всего 100, я бы сгенерировал случайное значение типа int и применил бы его к long.)
Hot Licks
java.util.Randomиспользуется только 48-битное распределение (см. подробности реализации), поэтому оно не будет иметь нормального распределения.
Джеффри Де Смет
1
В наши дни можно использовать org.apache.commons.lang3.RandomUtils # nextLong.
действительно хорошая

Ответы:

149

Начиная с Java 7 (или Android API Level 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;
}
kennytm
источник
1
У меня есть некоторые проблемы с "2 ^ х проверки" часть. Любые идеи?
Вилюс Нормантас
@Vilius: проверка 2 ^ x только ускоряет генерацию, потому что непосредственное использование rng.nextLong() % nдаст одинаковые значения (предположим, что все биты хороши). Вы можете игнорировать эту часть, если хотите.
Кеннитм
Если я хочу m <= x <= n, как бы вы изменили свое решение?
Би Джей Питер ДеЛаКруз
6
@BJPeterDeLaCruz: случайное число между mи nможет быть получено со случайным числом между 0и n-m, затем добавьте m.
Кеннитм
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методы и позволяет вам выбрать начальное число, если вы хотите воспроизводимую последовательность чисел.

Алекс - GlassEditor.com
источник
5
Этот ответ намного проще и, следовательно, более полезен, чем наиболее проголосовавший.
Юрин
2
Для тех, кто разрабатывает для Android, обратите внимание, что он доступен только из API 21 (Lollipop, Android 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
6
Пожалуйста, никогда не используйте это. Выход не является равномерным на всех.
Навин
2
Самая большая проблема заключается в том, что округление сделает самый младший бит очень неоднородным. Кроме того, boundдолжно быть меньше, чем наибольшее целое число, которое может быть закодировано в двойном, 2 ^ 53.
Александр Дубинский
12

Методы выше работают отлично. Если вы используете Apache Commons (org.apache.commons.math.random), проверьте RandomData. У него есть метод: nextLong (длинный нижний, длинный верхний)

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)

mawaldne
источник
3
Для потомков: RandomData устарела в 4.0. Использовать commons.apache.org/proper/commons-math/apidocs/org/apache/…
Михаил Тончев
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
А также проверьтеif (nextLong() >= 0)
khcpietro
6
К вашему сведению: это не всегда дает равномерное распределение, и это действительно плохо для некоторых больших диапазонов. Например, если min = 0и max = 2 * (MAX_LONG / 3), тогда у вас вдвое больше шансов получить значение, [0, MAX_LONG / 3]чем у вас [MAX_LONG / 3, 2 * (MAX_LONG / 3)].
Ник
Этот код не будет работать. Если nextLongвозвращает отрицательное значение, остаток будет отрицательным, и значение будет вне диапазона.
Арно
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;
  }
}
Enrice
источник
Я знаю, что это старый ответ и вряд ли будет использоваться, но эта часть явно некорректна: во- if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } первых, с помощью модульных тестов легко показать, что это на самом деле не дает числа в диапазоне [0, bound). Во-вторых, он излишне сложен: r = r & mон достиг бы желаемого результата, и это в основном то, что делает текущая реализация Java 8. Возможно, что реализация была другой, когда был написан этот ответ, но это не могло быть тем, что показано.
Е. Бишоп
3

Если вам нужен равномерно распределенный псевдослучайный тип long в диапазоне [0, m), попробуйте использовать оператор по модулю и метод абсолютного значения в сочетании с nextLong()методом, как показано ниже:

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

Где randтвой случайный объект?

Оператор по модулю делит два числа и выводит остаток от этих чисел. Например, 3 % 2это1 потому , что остальная часть 3 и 2 равно 1.

Так как nextLong()генерирует равномерно распределенное псевдослучайное длинное в диапазоне [- (2 ^ 48), 2 ^ 48) (или где-то в этом диапазоне), вам нужно будет принять его абсолютное значение. Если вы этого не сделаете, по модулю nextLong()метода с вероятностью 50% можно вернуть отрицательное значение, выходящее за пределы диапазона [0, m).

Сначала вы запрашивали равномерно распределенный псевдослучайный тип в диапазоне [0,100). Следующий код делает это:

Math.abs(rand.nextLong()) % 100;
TheGamePlayer 40
источник
1
по модулю предвзято, не используйте его для случайного stackoverflow.com/a/10984975/1166266
Сирены
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;
}

?

разработчик Android
источник
Все в порядке, кроме частей, которые принадлежат каркасу (я думаю).
Дамир Олехар
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;

}
Арпан Сайни
источник
Когда я сбрасываю долго мин = 1л; длинный максимум = 10 л; Полученное случайное число выходит за пределы максимального значения!
Радж Раджен
Это должно быть random.nextLong ()% (max - min) + min;
Джей Джодивал
2

Из Java 8 API

Может быть проще взять фактическую реализацию из документа API 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 () должен возвращать все возможные значения. Кстати, как длина начального числа связана с распределением значений?
Вилюс Нормантас
0

Методы с использованием r.nextDouble()должны использовать:

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


long number = x+(((long)r.nextDouble())*(y-x));
J Low
источник
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;
}
XXX
источник
1
Вы не должны создавать Randomэкземпляры в hoc, вы не должны перехватывать 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());

Это должно работать

Liash101
источник
-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