Java: zufällige lange Zahl im Bereich 0 <= x <n


135

Die Zufallsklasse verfügt über eine Methode zum Generieren von Zufalls-Int in einem bestimmten Bereich. Beispielsweise:

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

Dies würde eine int-Zahl erzeugen, die größer oder gleich 0 und kleiner als 100 ist. Ich möchte genau dasselbe mit einer langen Zahl tun.

long y = magicRandomLongGenerator(100);

Die zufällige Klasse hat nur nextLong (), erlaubt jedoch nicht, den Bereich festzulegen.


Verwandte, kann nützlich sein: stackoverflow.com/questions/2290057/…
TJ Crowder

1
Hast du darüber nachgedacht, nur deinen langen Zufall zu bekommen und den Mod deiner Reichweite zu nehmen? (Wenn der Bereich nur 100 ist, würde ich natürlich einen int-Zufall erzeugen und ihn zu lang wirken.)
Hot Licks

java.util.RandomVerwendet nur eine 48-Bit-Verteilung (siehe Implementierungsdetails), daher gibt es keine Normalverteilung.
Geoffrey De Smet

1
In der heutigen Zeit könnte man die Verwendung von org.apache.commons.lang3.RandomUtils # nextLong in Betracht ziehen.
wirklich schön

Antworten:


149

Ab Java 7 (oder Android API Level 21 = 5.0+) können Sie direkt ThreadLocalRandom.current().nextLong(n)(für 0 ≤ x <n) und ThreadLocalRandom.current().nextLong(m, n)(für m ≤ x <n) verwenden. Siehe @ Alex ‚s Antwort zum Detail.


Wenn Sie mit Java 6 (oder Android 4.x) nicht weiterkommen , müssen Sie eine externe Bibliothek verwenden ( org.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1)siehe z . B. die Antwort von @mawaldne ) oder eine eigene implementieren nextLong(n).

Laut https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.html nextInt ist implementiert als

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

Wir können dies also ändern, um Folgendes auszuführen 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
Ich habe einige Probleme mit dem Teil "2 ^ x Prüfen". Irgendwelche Ideen?
Vilius Normantas

@Vilius: Die 2 ^ x-Prüfung beschleunigt nur die Generierung, da bei direkter Verwendung rng.nextLong() % neinheitliche Werte erhalten werden (vorausgesetzt, alle Bits sind gut). Sie können diesen Teil ignorieren, wenn Sie möchten.
Kennytm

Wenn Sie möchten m <= x <= n, wie würden Sie Ihre Lösung ändern?
BJ Peter DeLaCruz

6
@BJPeterDeLaCruz: Eine Zufallszahl zwischen mund nkann mit einer Zufallszahl zwischen 0und erhalten werden n-m, dann addieren m.
Kennytm

83

ThreadLocalRandom

ThreadLocalRandomhat eine nextLong(long bound)Methode.

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

Es hat auch, nextLong(long origin, long bound)wenn Sie einen anderen Ursprung als 0 benötigen. Übergeben Sie den Ursprung (einschließlich) und die Grenze (exklusiv).

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

SplittableRandomhat die gleichen nextLongMethoden und ermöglicht es Ihnen, einen Startwert auszuwählen, wenn Sie eine reproduzierbare Folge von Zahlen wünschen.


5
Diese Antwort ist viel einfacher und daher nützlicher als die am häufigsten gewählte.
Yurin

2
Beachten Sie für diejenigen, die für Android entwickeln, dass es nur von API 21 (Lollipop, Android 5.0) verfügbar ist: developer.android.com/reference/java/util/concurrent/…
Android-Entwickler

75

Die Standardmethode zum Generieren einer Zahl (ohne Dienstprogrammmethode) in einem Bereich besteht darin, einfach das Doppelte mit dem Bereich zu verwenden:

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

gibt Ihnen eine lange zwischen 0 (einschließlich) und Bereich (exklusiv). Ebenso, wenn Sie eine Zahl zwischen x und y wollen:

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

erhalten Sie eine lange von 1234567 (einschließlich) bis 123456789 (exklusiv)

Hinweis: Überprüfen Sie die Klammern, da das Casting auf Long eine höhere Priorität als die Multiplikation hat.


5
Meine erste Idee war genau das. Aber es scheint ein bisschen unelegant zu sein. Und ich mache mir Sorgen um die Gleichmäßigkeit der Verteilung (es ist nicht so, dass ich es wirklich brauche, ich möchte es nur richtig machen)
Vilius Normantas

6
Bitte benutzen Sie dies niemals. Die Ausgabe ist überhaupt nicht gleichmäßig.
Navin

2
Das größte Problem ist, dass durch Rundung das unterste Bit sehr ungleichmäßig wird. Außerdem boundmuss es kleiner sein als die größte Ganzzahl, die in einem Doppel codiert werden kann, 2 ^ 53.
Aleksandr Dubinsky

12

Die oben genannten Methoden funktionieren hervorragend. Wenn Sie Apache Commons (org.apache.commons.math.random) verwenden, lesen Sie RandomData. Es gibt eine Methode: nextLong (lang unten, lang oben)

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
Für die Nachwelt: RandomData ist in 4.0 veraltet. Verwenden Sie commons.apache.org/proper/commons-math/apidocs/org/apache/…
Michael

11

Verwenden Sie den Operator '%'

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

Mit dem Operator '%' nehmen wir den Rest, geteilt durch Ihren Maximalwert. Dies lässt uns nur Zahlen von 0 (einschließlich) bis zum Divisor (exklusiv).

Beispielsweise:

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

Das ist schön, aber Sie sollten überprüfenif (max == min)
khcpietro

Und überprüfen Sie auchif (nextLong() >= 0)
khcpietro

6
Zu Ihrer Information: Dies ergibt nicht immer eine gleichmäßige Verteilung, und es ist für einige große Bereiche wirklich schlecht. Wenn zum Beispiel min = 0und max = 2 * (MAX_LONG / 3), dann erhalten Sie doppelt so häufig einen Wert [0, MAX_LONG / 3]wie einen [MAX_LONG / 3, 2 * (MAX_LONG / 3)].
Nick

Dieser Code funktioniert nicht. Wenn nextLongein negativer Wert zurückgegeben wird, ist der Rest negativ und der Wert liegt außerhalb des Bereichs.
Arnaud

3

Weitere Verbesserung der Antwort von kennytm: Eine Implementierung der Unterklasse unter Berücksichtigung der tatsächlichen Implementierung in Java 8 wäre:

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

Ich weiß, dass dies eine alte Antwort ist und wahrscheinlich nicht verwendet wird, aber dieser Teil ist nachweislich falsch: if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } Erstens ist es mit Komponententests leicht zu zeigen, dass dies tatsächlich keine Zahlen im Bereich [0, gebunden] erzeugt. Zweitens ist es unnötig aufwendig: r = r & mwürde das gewünschte Ergebnis erzielen, und genau das macht die aktuelle Java 8-Implementierung. Es ist möglich, dass die Implementierung anders war, als diese Antwort geschrieben wurde, aber es kann nicht das gewesen sein, was gezeigt wurde.
E. Bishop

3

Wenn Sie eine gleichmäßig verteilte Pseudozufallslänge im Bereich von [0, m) wünschen , verwenden Sie den Modulo-Operator und die Absolutwertmethode in Kombination mit der folgenden nextLong()Methode:

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

Wo randist dein zufälliges Objekt?

Der Modulo-Operator teilt zwei Zahlen und gibt den Rest dieser Zahlen aus. Zum Beispiel 3 % 2ist , 1da der Rest von 3 und 2 : 1.

Da nextLong()ein gleichmäßig verteiltes Pseudozufall im Bereich von [- (2 ^ 48), 2 ^ 48) (oder irgendwo in diesem Bereich) erzeugt wird, müssen Sie den absoluten Wert davon nehmen. Wenn Sie dies nicht tun, hat das Modulo der nextLong()Methode eine 50% ige Chance, einen negativen Wert zurückzugeben, der außerhalb des Bereichs [0,] liegt m.

Was Sie ursprünglich angefordert haben, war eine gleichmäßig verteilte Pseudozufallslänge im Bereich von [0,100]. Der folgende Code tut dies:

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

1
Modulo ist voreingenommen, verwenden Sie es nicht für zufällige stackoverflow.com/a/10984975/1166266
Sirenen

2

Wie wäre es damit:

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

?


Ist in Ordnung, außer den Teilen, die zu einem Framework gehören (denke ich).
Damir Olejar

2

Die folgende Methode gibt einen Wert zwischen 10000000000 und 9999999999 zurück

long min = 1000000000L
long max = 9999999999L    

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

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

}

Wenn ich lange zurücksetze min = 1L; lang max = 10 l; Die resultierende Zufallszahl geht über den Maximalwert hinaus!
Raj Rajen

Es sollte random.nextLong ()% (max - min) + min sein;
Jay Jodiwal

2

Von der Java 8 API

Es könnte einfacher sein, die tatsächliche Implementierung aus dem API-Dokument https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- zu übernehmen, für das sie es verwenden Longs Stream generieren. Und Ihr Ursprung kann wie in der Frage "0" sein.

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

Von der Seite auf Random :

Die Methode nextLong wird von der Klasse Random wie folgt implementiert:

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

Da die Klasse Random einen Startwert mit nur 48 Bit verwendet, gibt dieser Algorithmus nicht alle möglichen langen Werte zurück.

Wenn Sie also eine erhalten möchten, erhalten LongSie bereits nicht die volle 64-Bit-Reichweite.

Ich würde vorschlagen, dass Sie, wenn Sie eine Reichweite haben, die nahe einer Potenz von 2 liegt, die Longin diesem Snippet wie folgt aufbauen:

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

um beispielsweise einen 35-Bit-Bereich zu erhalten.


2
In der Dokumentation heißt es jedoch: "Alle 2 ^ 64 möglichen langen Werte werden mit (ungefähr) gleicher Wahrscheinlichkeit erzeugt." Anscheinend sollte die nextLong () -Methode alle möglichen Werte zurückgeben. Übrigens, wie lang ist der Startwert mit der Verteilung der Werte?
Vilius Normantas

0

Die Methoden, die r.nextDouble()das verwenden, sollten Folgendes verwenden:

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
Sie sollten keine RandomInstanzen bei hoc erstellen , Sie sollten keine Throwables oder andere Ausnahmen abfangen, wenn dies nicht erforderlich ist. Sie sollten Fehler mit einer Art Protokollierungsframework (z. B. SLF4J) protokollieren, anstatt sie zu verwenden printStackTrace.
Boguś

0

Wenn Sie Java-Streams verwenden können, können Sie Folgendes versuchen:

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

Dadurch werden für Longs Zahlen im angegebenen Bereich generiert.


0
import java.util*;

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

Das sollte funktionieren


-4

// Verwenden Sie die Systemzeit als Startwert, um eine gute Zufallszahl zu erhalten

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

// Schleife, bis eine Zahl größer oder gleich 0 und kleiner als n ist


1
Dies kann äußerst unzulänglich sein. Was nist, wenn 1 ist oder 2 sagen? Die Schleife führt viele Iterationen durch.
Magnilex
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.