Java: випадкове довге число в діапазоні 0 <= x <n


135

Випадковий клас має метод для генерування випадкових int у заданому діапазоні. Наприклад:

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

Це створило б число int більше або дорівнює 0 і менше 100. Я хотів би зробити те саме, що і з довгим числом.

long y = magicRandomLongGenerator(100);

Випадковий клас має лише nextLong (), але він не дозволяє встановити діапазон.


Пов’язані, можуть бути корисні: stackoverflow.com/questions/2290057/…
TJ Crowder

1
Чи розглядали ви, як просто отримати свій довгий випадковий вибір і взяти мод свого діапазону? (Звичайно, якщо діапазон становить лише 100, я б створив int випадковим чином і подав його на довгий.)
Hot Licks

java.util.Randomвикористовується лише 48-бітний розподіл (див. деталі реалізації), тому нормальний розподіл не матиме.
Джеффрі Де Смет

1
У сучасні дні можна подумати про використання org.apache.commons.lang3.RandomUtils # nextLong.
realnice

Відповіді:


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: Перевірка 2 ^ x просто робить генерацію швидшою, оскільки безпосередньо за допомогою rng.nextLong() % nбуде дано однакові значення (припустимо, всі біти хороші). Ви можете ігнорувати цю частину, якщо хочете.
kennytm

Якщо я хочу m <= x <= n, як би ви змінили своє рішення?
BJ Peter DeLaCruz

6
@BJPeterDeLaCruz: Випадкове число між mі nможе бути отримане із випадковим числом між, 0а n-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
Ця відповідь набагато простіша і, отже, корисніша за найбільш проголосовану.
Юрін

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
Моя перша ідея полягала саме в цьому. Але це здається трохи неелегантним. І я переживаю за рівномірність розподілу (це не те, що мені це дуже потрібно, я просто хочу це зробити правильно)
Віліус Нормантас

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)


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
FYI: Це не завжди дає рівномірний розподіл, і це дуже погано для деяких великих діапазонів. Наприклад, якщо 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;
  }
}

Я знаю, що це стара відповідь і навряд чи вона буде використана, але ця частина очевидно неправильна: if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } По-перше, легко показати за допомогою одиничних тестів, що це насправді не дає чисел у діапазоні [0, обмежений). По-друге, це надмірно детально: r = r & mдосягти бажаного результату, і це в основному те, що робить поточна реалізація Java 8. Можливо, що реалізація була іншою, коли ця відповідь була написана, але це не могло бути тим, що показано.
Єпископ Е.

3

Якщо ви хочете, щоб рівномірно розподілений псевдовипадковий довгий в діапазоні [0, m), спробуйте скористатися оператором модуля та методом абсолютного значення в поєднанні з nextLong()методом, як показано нижче:

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

Де randваш об'єкт Random.

Оператор модуля ділить два числа і виводить залишки цих чисел. Наприклад, 3 % 2це 1тому , що інша частина 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
Сирени

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

?


Це добре, за винятком частин, які належать до рамки (я здогадуюсь).
Дамір Олежар

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 л; Отримане випадкове число виходить за межі максимального значення!
Raj Rajen

Він повинен бути випадковим.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

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екземпляри hoc, ви не повинні ловити Throwables або інші винятки, якщо вони не потрібні, ви повинні записувати помилки з якоюсь рамкою журналу (наприклад, SLF4J), а не використовувати printStackTrace.
Boguś

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.