Java: số dài ngẫu nhiên trong phạm vi 0 <= x <n


135

Lớp ngẫu nhiên có một phương thức để tạo int ngẫu nhiên trong một phạm vi nhất định. Ví dụ:

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

Điều này sẽ tạo ra một số int nhiều hơn hoặc bằng 0 và nhỏ hơn 100. Tôi muốn làm chính xác như vậy với số dài.

long y = magicRandomLongGenerator(100);

Lớp ngẫu nhiên chỉ có nextLong (), nhưng nó không cho phép đặt phạm vi.


Liên quan, có thể hữu ích: stackoverflow.com/questions/2290057/ từ
TJ Crowder

1
Bạn đã xem xét chỉ nhận được ngẫu nhiên dài của bạn và lấy mod của phạm vi của bạn? (Tất nhiên, nếu phạm vi chỉ là 100, tôi sẽ tạo ra một int ngẫu nhiên và kéo dài nó.)
Hot Licks

java.util.Randomchỉ sử dụng phân phối 48 bit (xem chi tiết triển khai), do đó, nó sẽ không có phân phối bình thường.
Geoffrey De Smet

1
Trong thời hiện đại, người ta có thể cân nhắc sử dụng org.apache.commons.lang3.RandomUtils # nextLong.
thực sự là

Câu trả lời:


149

Bắt đầu từ Java 7 (hoặc API Android cấp 21 = 5.0+), bạn có thể trực tiếp sử dụng ThreadLocalRandom.current().nextLong(n)(cho 0 ≤ x <n) và ThreadLocalRandom.current().nextLong(m, n)(cho m ≤ x <n). Xem câu trả lời của @Alex để biết chi tiết.


Nếu bạn bị mắc kẹt với Java 6 (hoặc Android 4.x), bạn cần sử dụng thư viện bên ngoài (ví dụ: org.apache.commons.math3.random.RandomDataGenerator.getRandomGenerator().nextLong(0, n-1)xem câu trả lời của @mawaldne ) hoặc tự thực hiện nextLong(n).

Theo https://docs.oracle.com/javase/1.5.0/docs/api/java/util/Random.html nextInt được triển khai dưới dạng

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

Vì vậy, chúng tôi có thể sửa đổi điều này để thực hiện 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
Tôi đang gặp một số vấn đề với phần "2 ^ x check". Có ý kiến ​​gì không?
Vilius Normantas

@Vilius: Việc kiểm tra 2 ^ x chỉ giúp việc tạo nhanh hơn vì sử dụng trực tiếp rng.nextLong() % nsẽ cho các giá trị đồng nhất (giả sử tất cả các bit đều tốt). Bạn có thể bỏ qua phần đó nếu bạn muốn.
kennytm

Nếu tôi muốn m <= x <= n, bạn sẽ sửa đổi giải pháp của bạn như thế nào?
Bj Peter DeLaCruz

6
@BJPeterDeLaCruz: Một số ngẫu nhiên giữa mncó thể được lấy với một số ngẫu nhiên giữa 0n-msau đó thêm m.
kennytm

84

ThreadLocalRandom

ThreadLocalRandomcó một nextLong(long bound)phương pháp.

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

Nó cũng có nextLong(long origin, long bound) nếu bạn cần một nguồn gốc khác 0. Chuyển nguồn gốc (bao gồm) và ràng buộc (độc quyền).

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

SplittableRandomcó cùng nextLongphương thức và cho phép bạn chọn một hạt giống nếu bạn muốn một chuỗi số có thể lặp lại.


5
Câu trả lời này đơn giản hơn nhiều và do đó hữu ích hơn câu trả lời nhiều nhất.
yurin

2
Đối với những người đang phát triển cho Android, hãy lưu ý rằng nó chỉ khả dụng từ API 21 (Lollipop, Android 5.0): developer.android.com/reference/java/util/concản/iêu
nhà phát triển Android

75

Phương thức tiêu chuẩn để tạo một số (không có phương thức tiện ích) trong một phạm vi là chỉ sử dụng số kép với phạm vi:

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

sẽ cung cấp cho bạn một khoảng dài từ 0 (bao gồm) và phạm vi (độc quyền). Tương tự nếu bạn muốn một số giữa x và y:

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

sẽ cung cấp cho bạn một thời gian dài từ 1234567 (bao gồm) đến 123456789 (độc quyền)

Lưu ý: kiểm tra dấu ngoặc đơn, bởi vì truyền từ lâu có mức độ ưu tiên cao hơn phép nhân.


5
Ý tưởng đầu tiên của tôi là chính xác điều này. Nhưng nó có vẻ là một chút không phù hợp. Và tôi lo lắng về tính đồng nhất của phân phối (không phải là tôi thực sự cần nó, tôi chỉ muốn làm đúng)
Vilius Normantas

6
Xin đừng bao giờ sử dụng cái này. Đầu ra không thống nhất chút nào.
Navin

2
Vấn đề lớn nhất là làm tròn sẽ làm cho bit thấp nhất rất không hình thành. Ngoài ra, boundsẽ phải nhỏ hơn số nguyên lớn nhất có thể được mã hóa thành gấp đôi, 2 ^ 53.
Alexanderr Dubinsky

12

Các phương pháp trên làm việc tuyệt vời. Nếu bạn đang sử dụng commache commons (org.apache.commons.math.random), hãy xem RandomData. Nó có một phương thức: nextLong (dài dưới, dài trên)

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
Đối với hậu thế: RandomData không được dùng nữa trong 4.0. Sử dụng commons.apache.org/proper/commons-math/apidocs/org/apache/
mẹo

11

Sử dụng toán tử '%'

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

Bằng cách sử dụng toán tử '%', chúng tôi sẽ lấy phần còn lại khi chia cho giá trị tối đa của bạn. Điều này khiến chúng tôi chỉ có các số từ 0 (đã bao gồm) đến số chia (độc quyền).

Ví dụ:

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

Điều này là tốt, nhưng bạn nên kiểm traif (max == min)
khcpietro

Và cũng kiểm traif (nextLong() >= 0)
khcpietro

6
FYI: Điều này không phải lúc nào cũng phân phối đồng đều, và nó thực sự tệ cho một số phạm vi lớn. Ví dụ, nếu min = 0max = 2 * (MAX_LONG / 3), sau đó bạn gấp đôi khả năng để có được một giá trị trong [0, MAX_LONG / 3]khi bạn là để có được một trong [MAX_LONG / 3, 2 * (MAX_LONG / 3)].
Nick

Mã này sẽ không hoạt động. nếu nextLongtrả về giá trị âm, phần còn lại sẽ âm và giá trị sẽ nằm ngoài phạm vi.
Arnaud

3

Cải thiện hơn nữa câu trả lời của kennytm: Việc triển khai lớp con thực hiện triển khai thực tế trong Java 8 sẽ là:

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

Tôi biết đây là một câu trả lời cũ và không có khả năng sử dụng, nhưng phần này không chính xác: if ((bound & m) == 0) { r = (bound * r) >> (Long.SIZE - 1); } Đầu tiên, thật dễ dàng để hiển thị với các bài kiểm tra đơn vị rằng điều này không thực sự tạo ra các số trong phạm vi [0, bị ràng buộc). Thứ hai, nó được xây dựng một cách không cần thiết: r = r & msẽ hoàn thành kết quả mong muốn và về cơ bản đó là cách triển khai Java 8 hiện tại. Có thể việc triển khai đã khác khi câu trả lời này được viết, nhưng nó không thể là những gì được hiển thị.
E. Giám mục

3

Nếu bạn muốn một giả danh phân phối đồng đều dài trong phạm vi [0, m), hãy thử sử dụng toán tử modulo và phương pháp giá trị tuyệt đối kết hợp với nextLong()phương thức như dưới đây:

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

Đâu randlà đối tượng ngẫu nhiên của bạn.

Toán tử modulo chia hai số và xuất ra phần còn lại của các số đó. Ví dụ, 3 % 21 vì phần còn lại của 3 và 2 là 1.

nextLong()tạo ra một giả ngẫu nhiên phân phối đồng đều dài trong phạm vi [- (2 ^ 48), 2 ^ 48) (hoặc ở đâu đó trong phạm vi đó), bạn sẽ cần lấy giá trị tuyệt đối của nó. Nếu bạn không, modulo của nextLong()phương thức có 50% cơ hội trả về giá trị âm, nằm ngoài phạm vi [0,m ).

Những gì bạn yêu cầu ban đầu là một giả ngẫu nhiên phân phối đồng đều dài trong phạm vi [0,100). Các mã sau đây làm như vậy:

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

1
modulo bị sai lệch, không sử dụng nó cho stackoverflow
nhiên.com / a / 10984975/1166266

2

Còn cái này thì sao:

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

?


Không sao, ngoại trừ những phần thuộc về một khung (tôi đoán).
Damir Olejar

2

Phương pháp bên dưới sẽ trả về cho bạn một giá trị trong khoảng từ 10000000000 đến 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;

}

Khi tôi đặt lại lâu min = 1L; dài tối đa = 10L; Số ngẫu nhiên kết quả vượt quá Giá trị tối đa!
Raj Rajen

Nó phải là ngẫu nhiên.nextLong ()% (max - min) + min;
Jay Jodiwal

2

Từ API Java 8

Có thể dễ dàng thực hiện triển khai thực tế hơn từ API doc https://docs.oracle.com/javase/8/docs/api/java/util/Random.html#longs-long-long-long- họ đang sử dụng nó để tạo luồng dài. Và nguồn gốc của bạn có thể là "0" như trong câu hỏi.

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

Từ trang trên Random :

Phương thức nextLong được lớp Thực hiện ngẫu nhiên như thể bởi:

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

Vì lớp Random sử dụng một hạt giống chỉ có 48 bit, thuật toán này sẽ không trả về tất cả các giá trị dài có thể.

Vì vậy, nếu bạn muốn có được một Long, bạn sẽ không nhận được phạm vi 64 bit đầy đủ.

Tôi sẽ đề nghị rằng nếu bạn có một phạm vi nằm gần sức mạnh 2, bạn hãy xây dựng Longnhư trong đoạn trích đó, như thế này:

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

để có được một phạm vi 35 bit, ví dụ.


2
Nhưng tài liệu nói rằng "Tất cả 2 ^ 64 giá trị dài có thể được tạo ra với (xấp xỉ) xác suất bằng nhau." Vì vậy, rõ ràng phương thức nextLong () sẽ trả về tất cả các giá trị có thể .. Btw, độ dài của hạt có liên quan đến phân phối giá trị như thế nào?
Vilius Normantas

0

Các phương pháp sử dụng r.nextDouble()nên sử dụng:

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
Bạn không nên tạo các Randomtrường hợp tại hoc, bạn không nên bắt gặp Throwablecác trường hợp ngoại lệ khác nếu không cần thiết, bạn nên ghi nhật ký lỗi với một số loại khung ghi nhật ký (ví dụ SLF4J) thay vì sử dụng printStackTrace.
Boguś

0

Nếu bạn có thể sử dụng các luồng java, bạn có thể thử như sau:

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

Điều này sẽ tạo ra các số trong phạm vi nhất định trong thời gian dài.


0
import java.util*;

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

Điều này sẽ làm việc


-4

// sử dụng thời gian hệ thống làm giá trị hạt giống để có được số ngẫu nhiên tốt

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

// Lặp cho đến khi nhận được một số lớn hơn hoặc bằng 0 và nhỏ hơn n


1
Điều này có thể cực kỳ vô dụng. Nếu nlà 1, hay nói 2 thì sao? Vòng lặp sẽ thực hiện nhiều lần lặp.
Magnilex
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.