Java: 0 <= x <n aralığında rastgele uzun sayı


135

Rasgele sınıfın belirli bir aralıkta rasgele int üretme yöntemi vardır. Örneğin:

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

Bu, 0'a 100 veya daha küçük bir int numarası üretecektir. Uzun sayıyla tam olarak aynı şeyi yapmak istiyorum.

long y = magicRandomLongGenerator(100);

Rasgele sınıfın yalnızca nextLong () öğesi vardır, ancak aralığın ayarlanmasına izin vermez.



1
Sadece uzun rasgele olmayı ve aralığınızın modunu almayı düşündünüz mü? (Tabii ki, aralık sadece 100 ise rastgele bir int üretip uzun süre
dökerdim

java.util.Randomyalnızca 48 bit dağıtım kullanır (uygulama ayrıntılarına bakın), bu nedenle normal bir dağıtım olmaz.
Geoffrey De Smet

1
Modern günlerde org.apache.commons.lang3.RandomUtils # nextLong kullanmayı düşünebilirsiniz.
reallynice

Yanıtlar:


149

Java 7'den (veya Android API Seviye 21 = 5.0+) başlayarak ThreadLocalRandom.current().nextLong(n)(0 ≤ x <n için) ve ThreadLocalRandom.current().nextLong(m, n)(m ≤ x <n için) doğrudan kullanabilirsiniz . Ayrıntılar için @Alex'in cevabına bakınız .


Java 6 (veya Android 4.x) ile sıkışmışsanız, harici bir kitaplık kullanmanız gerekir (örneğin org.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1), @mawaldne'nin cevabına bakın) veya kendi kitaplığınızı uygulamanız gerekir nextLong(n).

Göre https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.html nextInt olarak uygulanır

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

Bu yüzden bunu gerçekleştirmek için değiştirebiliriz 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 denetleme" bölümü ile ilgili bazı sorunlar yaşıyorum. Herhangi bir fikir?
Vilius Normantas

@Vilius: 2 ^ x denetimi nesli daha hızlı yapar çünkü doğrudan kullanarak rng.nextLong() % ntekdüze değerler verilecektir (tüm bitlerin iyi olduğunu varsayın). İsterseniz bu kısmı göz ardı edebilirsiniz.
kennytm

Ben istiyorsanız m <= x <= n, nasıl size çözüm değiştirebilir ki?
BJ Peter DeLaCruz

6
@BJPeterDeLaCruz: mve arasında rastgele bir sayı ile arasında rastgele bir sayı nelde edilebilir 0ve n-msonra ekleyin m.
kennytm

84

ThreadLocalRandom

ThreadLocalRandombir nextLong(long bound)yöntemi vardır.

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

Ayrıca nextLong(long origin, long bound)0 dışında bir kaynağa ihtiyacınız varsa vardır. Başlangıç (dahil) ve sınır (özel) geçin.

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

SplittableRandomaynı nextLongyöntemlere sahiptir ve tekrarlanabilir bir sayı dizisi istiyorsanız bir tohum seçmenize izin verir.


5
Bu cevap en çok oylanandan daha basit ve bu nedenle daha kullanışlıdır.
yurin

2
Android için geliştirenler için, yalnızca API 21'den (Lollipop, Android 5.0) kullanılabilir olduğuna dikkat edin: developer.android.com/reference/java/util/concurrent/…
android geliştiricisi

75

Bir aralıkta sayı (bir yardımcı program yöntemi olmadan) oluşturmak için standart yöntem, yalnızca aralıkla çiftini kullanmaktır:

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

size 0 (dahil) ve aralık (hariç) arasında uzun bir süre verir. Benzer şekilde x ile y arasında bir sayı istiyorsanız:

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

size 1234567 (dahil) ile 123456789 (özel) arasında uzun bir zaman verecektir

Not: parantezleri kontrol edin, çünkü uzun süre yayınlamanın çarpma işleminden daha yüksek önceliği vardır.


5
İlk fikrim aynen buydu. Ama biraz yetersiz görünüyor. Ve dağılımın homojenliği konusunda endişeliyim (gerçekten ihtiyacım olduğu için değil, sadece doğru yapmak istiyorum)
Vilius Normantas

6
Lütfen bunu asla kullanmayın. Çıktı hiç düzgün değil.
Navin

2
En büyük sorun, yuvarlamanın en düşük biti çok düzgün yapmamasıdır. Ayrıca, boundbir çift, 2 ^ 53 kodlanabilir en büyük tamsayıdan daha az olması gerekir.
Aleksandr Dubinsky

12

Yukarıdaki yöntemler harika çalışıyor. Apache commons (org.apache.commons.math.random) kullanıyorsanız RandomData'ya bakın. Bir yöntemi vardır: nextLong (uzun alt, uzun üst)

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
Posterity için: RandomData 4.0'da kullanımdan kaldırıldı. Commons.apache.org/proper/commons-math/apidocs/org/apache/…
Michael Tontchev

11

'%' Operatörünü kullanma

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

'%' Operatörünü kullanarak, maksimum değerinize bölündüğünüzde kalanını alırız. Bu bizi sadece 0 (dahil) ile bölen (özel) arasında sayılar bırakır.

Örneğin:

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

Bu güzel, ama kontrol etmelisinizif (max == min)
khcpietro

Ve ayrıca kontrolif (nextLong() >= 0)
khcpietro

6
FYI: Bu her zaman tek tip bir dağılım sağlamaz ve bazı geniş aralıklar için gerçekten kötüdür. Örneğin, eğer min = 0ve max = 2 * (MAX_LONG / 3), o zaman iki kat daha olasıdır bir değer elde etmek [0, MAX_LONG / 3]sen almak için ne kadar [MAX_LONG / 3, 2 * (MAX_LONG / 3)].
Nick

Bu kod çalışmaz. Eğer nextLongdöner negatif bir değer, kalan negatif olur ve değer aralığının dışında olacaktır.
Arnaud

3

Kennytm'in cevabını daha da iyileştirmek: Java 8'deki asıl uygulamayı dikkate alan bir alt sınıf uygulaması şöyle olacaktır:

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

Bunun eski bir cevap olduğunu ve kullanılmasının pek mümkün olmadığını biliyorum, ancak bu bölüm açıkça yanlış: if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } İlk olarak, birim testlerinde bunun aslında [0, bağlı) aralığında sayı üretmediğini göstermek kolaydır. İkincisi, gereksiz yere ayrıntılıdır: r = r & mistenen sonucu elde eder ve temel olarak mevcut Java 8 uygulamasının yaptığı budur. Bu cevap yazıldığında uygulamanın farklı olması mümkündür, ancak gösterilen şey bu olamaz.
E Bishop

3

[0, m) aralığında eşit olarak dağıtılmış bir takma ad istiyorsanız , modulo operatörünü ve aşağıdaki yöntemle birlikte mutlak değer yöntemini kullanmayı deneyin nextLong():

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

randRastgele nesneniz nerede .

Modulo operatörü iki sayıyı böler ve bu sayıların kalanını verir. Örneğin, 3 % 2bir 13 ve 2'nin kalan 1'dir.

Yana nextLong()aralığında uzun bir düzgün dağılmış yalancı rasgele oluşturur [- (2 ^ 48) ^ 48 2) (ya da bir yere bu aralıkta), bunun mutlak değerini almak gerekir. Bunu yapmazsanız, nextLong()yöntemin modulo'unun negatif değer döndürme şansı% 50'dir ve bu değer [0, m) aralığının dışındadır .

Başlangıçta talep ettiğiniz şey, [0,100) aralığında eşit olarak dağıtılmış bir takma ad idi. Aşağıdaki kod bunu yapar:

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

1
modulo önyargılı, rastgele için kullanmayın stackoverflow.com/a/10984975/1166266
Sirens

2

Buna ne dersin:

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

?


Bir çerçeveye ait parçalar hariç sanırım (sanırım).
Damir Olejar

2

Aşağıdaki yöntem size 10000000000 ila 9999999999 arasında bir değer döndürür

long min = 1000000000L
long max = 9999999999L    

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

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

}

Uzun min = 1L sıfırladığımda; uzun max = 10L; Ortaya çıkan rastgele sayı maksimum değerin ötesine geçer!
Raj Rajen

Random.nextLong ()% (max - min) + min olmalıdır;
Jay Jodiwal

2

Gönderen Java 8 API

Gerçek dokümanı API belgesinden almak daha kolay olabilir https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- bunu kullanıyorlar uzun ürün akışı üret. Ve kaynağınız sorudaki gibi "0" olabilir.

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

Rastgele sayfadan :

NextLong yöntemi, Rastgele sınıfı tarafından şu şekilde uygulanır:

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

Random sınıfı yalnızca 48 bitlik bir tohum kullandığından, bu algoritma olası tüm uzun değerleri döndürmez.

Eğer bir almak Longistiyorsanız, zaten 64 bit aralığının tamamını elde edemezsiniz.

2 gücüne yakın bir aralığınız varsa Long, bu snippet'teki gibi oluşturduğunuzu öneririm :

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

Örneğin, 35 bitlik bir aralık elde etmek için.


2
Ancak belgelerde "Tüm 2 ^ 64 olası uzun değerlerin tümü (yaklaşık) eşit olasılıkla üretilir." Görünüşe göre nextLong () yöntemi tüm olası değerleri döndürmelidir. Btw, tohumun uzunluğu değerlerin dağılımı ile ilişkilidir?
Vilius Normantas

0

Kullanılması r.nextDouble()gereken yöntemler :

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
Hoc'de Randomörnekler oluşturmamalısınız , gerekirse Throwables veya diğer istisnaları yakalamamalısınız , hataları kullanmak yerine bir tür günlükleme çerçevesiyle (yani SLF4J) günlüğe kaydetmelisiniz printStackTrace.
Boguś

0

Java akışlarını kullanabiliyorsanız aşağıdakileri deneyebilirsiniz:

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

Bu, verilen aralıkta uzun süre sayı üretecektir.


0
import java.util*;

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

Bu çalışmalı


-4

// iyi bir rasgele sayı elde etmek için sistem zamanını tohum değeri olarak kullan

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

// 0'dan büyük veya ona eşit ve n'den küçük bir sayı alana kadar döngü yap


1
Bu aşırı derecede yetersiz olabilir. n1 veya 2 ise ne olur ? Döngü birçok yineleme gerçekleştirecektir .
Magnilex
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.