Java: numero lungo casuale compreso nell'intervallo 0 <= x <n


135

La classe casuale ha un metodo per generare int casuali in un dato intervallo. Per esempio:

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

Ciò genererebbe un numero int maggiore o uguale a 0 e inferiore a 100. Vorrei fare esattamente lo stesso con un numero lungo.

long y = magicRandomLongGenerator(100);

La classe casuale ha solo nextLong (), ma non consente di impostare l'intervallo.


Correlati, possono essere utili: stackoverflow.com/questions/2290057/…
TJ Crowder

1
Hai preso in considerazione l'idea di ottenere il tuo long random e prendere la mod del tuo range? (Ovviamente, se l'intervallo è solo 100, produrrei un int casuale e lo lancerei a lungo.)
Hot Licks

java.util.Randomutilizza solo una distribuzione a 48 bit (vedere i dettagli dell'implementazione), quindi non avrà una distribuzione normale.
Geoffrey De Smet,

1
Ai giorni nostri si potrebbe considerare l'utilizzo di org.apache.commons.lang3.RandomUtils # nextLong.
reallynice

Risposte:


149

A partire da Java 7 (o Android API Level 21 = 5.0+) è possibile utilizzare direttamente ThreadLocalRandom.current().nextLong(n)(per 0 ≤ x <n) e ThreadLocalRandom.current().nextLong(m, n)(per m ≤ x <n). Vedi la risposta di @Alex per i dettagli.


Se sei bloccato con Java 6 (o Android 4.x) devi usare una libreria esterna (es org.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1). Vedi la risposta di @mawaldne ), o implementare la tua nextLong(n).

Secondo https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.html nextInt è implementato come

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

Quindi possiamo modificare questo per eseguire 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
Sto riscontrando alcuni problemi con la parte "2 ^ x verifica". Qualche idea?
Vilius Normantas,

@Vilius: il controllo 2 ^ x rende la generazione più veloce perché l'utilizzo diretto rng.nextLong() % ndarà valori uniformi (supponiamo che tutti i bit siano buoni). Puoi ignorare quella parte se vuoi.
kennytm,

Se lo desidero m <= x <= n, come modificheresti la tua soluzione?
BJ Peter DeLaCruz,

6
@BJPeterDeLaCruz: un numero casuale tra me npuò essere ottenuto con un numero casuale tra 0e n-m, quindi aggiungere m.
kennytm,

84

ThreadLocalRandom

ThreadLocalRandomha un nextLong(long bound)metodo.

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

Ha anche nextLong(long origin, long bound)se hai bisogno di un'origine diversa da 0. Passa l'origine (incluso) e il limite (esclusivo).

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

SplittableRandomha gli stessi nextLongmetodi e ti permette di scegliere un seme se vuoi una sequenza riproducibile di numeri.


5
Questa risposta è molto più semplice e quindi più utile di quella più votata.
yurin,

2
Per coloro che sviluppano per Android, nota che è disponibile solo da API 21 (Lollipop, Android 5.0): developer.android.com/reference/java/util/concurrent/…
sviluppatore Android

75

Il metodo standard per generare un numero (senza un metodo di utilità) in un intervallo è semplicemente usare il doppio con l'intervallo:

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

ti darà un intervallo tra 0 (incluso) e intervallo (esclusivo). Allo stesso modo se si desidera un numero compreso tra xey:

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

ti darà un periodo compreso tra 1234567 (incluso) e 123456789 (esclusivo)

Nota: controllare le parentesi, poiché il casting su long ha una priorità maggiore rispetto alla moltiplicazione.


5
La mia prima idea è stata esattamente questa. Ma sembra essere un po 'inelegante. E sono preoccupato per l'uniformità della distribuzione (non è che ne ho davvero bisogno, voglio solo farlo bene)
Vilius Normantas

6
Per favore, non usarlo mai. L'output non è affatto uniforme.
Navin,

2
Il problema più grande è che l'arrotondamento renderà il bit più basso molto non uniforme. Inoltre, bounddovrà essere inferiore all'intero più grande che può essere codificato in un doppio, 2 ^ 53.
Aleksandr Dubinsky,

12

I metodi sopra funzionano alla grande. Se stai usando i comuni di apache (org.apache.commons.math.random) dai un'occhiata a RandomData. Ha un metodo: 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
Per i posteri: RandomData è deprecato in 4.0. Usa commons.apache.org/proper/commons-math/apidocs/org/apache/…
Michael Tontchev

11

Utilizzare l'operatore '%'

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

Usando l'operatore '%', prendiamo il resto quando diviso per il tuo valore massimo. Questo ci lascia con solo numeri da 0 (incluso) al divisore (esclusivo).

Per esempio:

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

Questo è carino, ma dovresti controllareif (max == min)
khcpietro,

E controlla ancheif (nextLong() >= 0)
khcpietro il

6
Cordiali saluti: Questo non dà sempre una distribuzione uniforme, ed è davvero male per alcune grandi gamme. Ad esempio, se min = 0e max = 2 * (MAX_LONG / 3), allora hai il doppio delle probabilità di ottenere un valore rispetto a [0, MAX_LONG / 3]quello di ottenerne uno [MAX_LONG / 3, 2 * (MAX_LONG / 3)].
Nick,

Questo codice non funzionerà. se nextLongrestituisce un valore negativo, il resto sarà negativo e il valore sarà al di fuori dell'intervallo.
Arnaud,

3

Migliorare ulteriormente la risposta di kennytm: un'implementazione di una sottoclasse che tenga conto dell'implementazione effettiva in Java 8 sarebbe:

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

So che questa è una vecchia risposta e che è improbabile che venga utilizzata, ma questa parte è manifestamente errata: in if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } primo luogo, è facile dimostrare con i test unitari che in realtà questo non produce numeri nell'intervallo [0, associato). In secondo luogo, è inutilmente elaborato: r = r & mraggiungerebbe il risultato desiderato, ed è sostanzialmente quello che fa l'attuale implementazione di Java 8. È possibile che l'implementazione fosse diversa quando è stata scritta questa risposta, ma non può essere stato ciò che è stato mostrato.
E. Bishop,

3

Se si desidera uno pseudocasuale distribuito uniformemente nell'intervallo di [0, m), provare a utilizzare l'operatore modulo e il metodo del valore assoluto combinato con il nextLong()metodo come mostrato di seguito:

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

Dov'è il randtuo oggetto casuale.

L'operatore modulo divide due numeri e genera il resto di quei numeri. Ad esempio, 3 % 2è 1perché il resto di 3 e 2 è 1.

Poiché nextLong()genera un long pseudorandom distribuito uniformemente nell'intervallo di [- (2 ^ 48), 2 ^ 48) (o da qualche parte in quell'intervallo), dovrai prenderne il valore assoluto. In caso contrario, il modulo del nextLong()metodo ha una probabilità del 50% di restituire un valore negativo, che è fuori dall'intervallo [0, m).

Quello che inizialmente richiedevi era un lungo pseudocasuale distribuito uniformemente nell'intervallo di [0,100). Il codice seguente lo fa:

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

1
modulo è di parte, non utilizzarlo per stackoverflow casuale.com/a/10984975/1166266
Sirene

2

Cosa ne pensi di questo:

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

?


Va bene, tranne le parti che appartengono a un framework (immagino).
Damir Olejar,

2

Il metodo seguente restituirà un valore compreso tra 10000000000 e 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;

}

Quando ripristino long min = 1L; lungo max = 10L; Il numero casuale risultante va oltre il valore massimo!
Raj Rajen,

Dovrebbe essere random.nextLong ()% (max - min) + min;
Jay Jodiwal,

2

Da Java 8 API

Potrebbe essere più semplice ottenere l'implementazione effettiva dal documento API https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- lo stanno utilizzando per generare flussi long. E la tua origine può essere "0" come nella domanda.

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

Dalla pagina su Casuale :

Il metodo nextLong è implementato dalla classe Random come se da:

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

Poiché la classe Casuale utilizza un seme con solo 48 bit, questo algoritmo non restituirà tutti i possibili valori lunghi.

Quindi, se si desidera ottenere un Long, non si otterrà già l'intera gamma a 64 bit.

Suggerirei che se hai un intervallo che si avvicina a una potenza di 2, costruisci il Longcome in quello snippet, in questo modo:

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

per ottenere un intervallo di 35 bit, ad esempio.


2
Ma la documentazione dice "Tutti i 2 ^ 64 possibili valori lunghi sono prodotti con (approssimativamente) uguale probabilità". Quindi apparentemente il metodo nextLong () dovrebbe restituire tutti i possibili valori. A proposito, in che modo la lunghezza del seme è correlata alla distribuzione dei valori?
Vilius Normantas,

0

I metodi che usano il r.nextDouble()dovrebbero usare:

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
Non dovresti creare Randomistanze ad hoc, non dovresti prendere Throwablese altre eccezioni se non necessario, dovresti registrare gli errori con un qualche tipo di framework di registrazione (cioè SLF4J) invece di usare printStackTrace.
Boguś,

0

Se puoi utilizzare java stream, puoi provare quanto segue:

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

Questo genererà numeri nell'intervallo dato per lunghi.


0
import java.util*;

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

Questo dovrebbe funzionare


-4

// usa l'ora di sistema come valore seed per ottenere un buon numero casuale

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

// Ripeti finché non ottiene un numero maggiore o uguale a 0 e minore di n


1
Questo può essere estremamente insufficiente. Cosa succede se nè 1, o dire 2? Il loop eseguirà molte iterazioni.
Magnilex,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.