Điều gì với 181783497276652981 và 8682522807148012 trong Ngẫu nhiên (Java 7)?


112

Tại sao 1817834972766529818682522807148012được chọn trong Random.java?

Đây là mã nguồn có liên quan từ Java SE JDK 1.7:

/**
 * Creates a new random number generator. This constructor sets
 * the seed of the random number generator to a value very likely
 * to be distinct from any other invocation of this constructor.
 */
public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

private static long seedUniquifier() {
    // L'Ecuyer, "Tables of Linear Congruential Generators of
    // Different Sizes and Good Lattice Structure", 1999
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}

private static final AtomicLong seedUniquifier
    = new AtomicLong(8682522807148012L);

Vì vậy, việc gọi new Random()mà không có bất kỳ tham số hạt giống nào sẽ nhận "seed uniquifier" hiện tại và XOR nó với System.nanoTime(). Sau đó, nó sử dụng 181783497276652981để tạo một seed uniquifier khác để lưu trữ cho lần sau new Random()được gọi.

Các ký tự 181783497276652981L8682522807148012Lkhông được đặt trong hằng số, nhưng chúng không xuất hiện ở bất kỳ nơi nào khác.

Lúc đầu, nhận xét cho tôi một hướng dẫn dễ dàng. Tìm kiếm trực tuyến cho bài viết đó cho ra bài viết thực sự . 8682522807148012không xuất hiện trên giấy, nhưng 181783497276652981xuất hiện - dưới dạng một chuỗi con của một số khác,1181783497276652981 mà là 181783497276652981với một 1prepended.

Bài báo tuyên bố rằng 1181783497276652981 là một con số mang lại "giá trị" tốt cho một máy phát đồng dư tuyến tính. Con số này có phải đã được sao chép sai vào Java không? Có 181783497276652981một công trạng chấp nhận được không?

Và tại sao 8682522807148012 chọn?

Tìm kiếm trực tuyến cho một trong hai số không có lời giải thích nào, chỉ có trang này cũng thông báo số đã giảm 1ở phía trước 181783497276652981.

Có thể các số khác đã được chọn sẽ hoạt động tốt như hai số này? Tại sao hoặc tại sao không?


Tôi chỉ muốn chỉ ra rằng không có hằng số nào được đề cập (ngay cả những hằng số lớn hơn với những hằng số ở đầu) là quá lớn để phù hợp mặc dù phép nhân chắc chắn sẽ dẫn đến tràn.
nanofarad

6
8682522807148012là kế thừa của phiên bản trước của lớp, như có thể thấy trong các bản sửa đổi được thực hiện vào năm 2010 . Có 181783497276652981Lvẻ như thực sự là một lỗi đánh máy và bạn có thể gửi một báo cáo lỗi.
assylias

6
Đó là lỗi đánh máy, tức là lỗi hoặc một tính năng có động cơ không được tiết lộ. Bạn sẽ phải hỏi các tác giả. Bất cứ điều gì bạn nhận được ở đây sẽ chỉ là ý kiến ​​ít nhiều không được hiểu rõ. Nếu bạn cho rằng đó là lỗi, hãy gửi báo cáo lỗi.
Marquis of Lorne,

1
Đặc biệt là với các câu trả lời khác nhau, đây có thể là hai câu hỏi riêng biệt cho mỗi hằng số.
Mark Hurd

1
Thật buồn khi thấy một nút thắt cổ chai về khả năng mở rộng toàn cầu được xây dựng trong một lớp cơ bản như vậy. seedUniquifiercó thể trở nên cực kỳ cạnh tranh trên hộp 64 lõi. Một luồng cục bộ sẽ có thể mở rộng hơn.
usr

Câu trả lời:


57
  1. Con số này có phải đã được sao chép sai vào Java không?

    Vâng, có vẻ là một lỗi đánh máy.

  2. 181783497276652981 có bằng khen không?

    Điều này có thể được xác định bằng cách sử dụng thuật toán đánh giá được trình bày trong bài báo. Nhưng công của số "gốc" có lẽ cao hơn.

  3. Và tại sao 8682522807148012 lại được chọn?

    Có vẻ là ngẫu nhiên. Nó có thể là kết quả của System.nanoTime () khi mã được viết.

  4. Có thể các số khác đã được chọn sẽ hoạt động tốt như hai số này?

    Không phải mọi con số đều "tốt" như nhau. Vì vậy, không.

Chiến lược gieo hạt

Có sự khác biệt trong lược đồ hạt giống mặc định giữa các phiên bản khác nhau và việc triển khai JRE.

public Random() { this(System.currentTimeMillis()); }
public Random() { this(++seedUniquifier + System.nanoTime()); }
public Random() { this(seedUniquifier() ^ System.nanoTime()); }

Cách đầu tiên không được chấp nhận nếu bạn tạo nhiều RNG liên tiếp. Nếu thời gian tạo của chúng rơi vào cùng một khoảng mili giây, chúng sẽ cho các trình tự hoàn toàn giống nhau. (cùng một hạt giống => cùng một chuỗi)

Điều thứ hai là không an toàn chủ đề. Nhiều luồng có thể nhận được RNG giống hệt nhau khi khởi tạo cùng một lúc. Ngoài ra, hạt giống của những lần khởi tạo tiếp theo có xu hướng tương quan với nhau. Tùy thuộc vào độ phân giải bộ đếm thời gian thực của hệ thống, chuỗi hạt giống có thể tăng tuyến tính (n, n + 1, n + 2, ...). Như đã nêu trong Các hạt ngẫu nhiên cần phải khác nhau như thế nào? và bài báo tham khảo Các lỗi thường gặp trong quá trình khởi tạo bộ tạo số ngẫu nhiên , các hạt tương quan có thể tạo ra mối tương quan giữa các trình tự thực tế của nhiều RNG.

Cách tiếp cận thứ ba tạo ra các hạt giống được phân phối ngẫu nhiên và do đó không liên quan, ngay cả trên các luồng và các lần khởi tạo tiếp theo. Vì vậy, các tài liệu java hiện tại:

Hàm tạo này đặt hạt giống của bộ tạo số ngẫu nhiên thành một giá trị rất có thể khác với bất kỳ lệnh gọi nào khác của hàm tạo này.

có thể được mở rộng bằng "qua các chuỗi" và "không liên quan"

Chất lượng trình tự hạt giống

Nhưng tính ngẫu nhiên của trình tự gieo hạt chỉ tốt như RNG bên dưới. RNG được sử dụng cho chuỗi hạt giống trong việc triển khai java này sử dụng trình tạo đồng dư tuyến tính nhân (MLCG) với c = 0 và m = 2 ^ 64. (Mô-đun 2 ^ 64 được ngầm định bởi sự tràn của các số nguyên dài 64 bit) Do không c và lũy thừa của mô-đun 2, nên "chất lượng" (độ dài chu kỳ, tương quan bit, ...) bị hạn chế . Như bài báo nói, bên cạnh độ dài chu kỳ tổng thể, mỗi bit đơn lẻ đều có độ dài chu kỳ riêng, điều này giảm theo cấp số nhân đối với các bit ít quan trọng hơn. Do đó, các bit thấp hơn có mô hình lặp lại nhỏ hơn. (Kết quả của seedUniquifier () phải được đảo ngược bit, trước khi nó bị cắt ngắn thành 48 bit trong RNG thực tế)

Nhưng nó là nhanh chóng! Và để tránh các vòng lặp so sánh và thiết lập không cần thiết, thân vòng lặp phải nhanh. Điều này có lẽ giải thích cho việc sử dụng MLCG cụ thể này, không có phép cộng, không có xoring, chỉ cần một phép nhân.

Và bài báo được đề cập trình bày một danh sách các "số nhân" tốt cho c = 0 và m = 2 ^ 64, là 1181783497276652981.

Nói chung: A cho nỗ lực @ JRE-Developers;) Nhưng có một lỗi đánh máy. (Nhưng ai biết được, trừ khi ai đó đánh giá nó, có khả năng số 1 bị thiếu thực sự cải thiện RNG gieo hạt.)

Nhưng một số số nhân chắc chắn tệ hơn: "1" dẫn đến một chuỗi không đổi. "2" dẫn đến một chuỗi di chuyển một bit (tương quan bằng cách nào đó) ...

Mối tương quan giữa các chuỗi đối với RNG thực sự có liên quan đến Mô phỏng (Monte Carlo), trong đó nhiều chuỗi ngẫu nhiên được khởi tạo và thậm chí là song song. Vì vậy, một chiến lược gieo hạt tốt là cần thiết để chạy mô phỏng "độc lập". Do đó, tiêu chuẩn C ++ 11 đưa ra khái niệm Chuỗi hạt giống để tạo ra các hạt giống không tương quan.


3
Ít nhất thì nó vẫn kỳ lạ, nếu họ đã bỏ đi một số ít nhất thay vì một số quan trọng nhất thì mỗi phép nhân sẽ mất đi một chút cho đến khi cuối cùng (sau 62 bước) seedUniquifiertrở nên bị mắc kẹt ở số không.
harold

9

Nếu bạn coi rằng phương trình được sử dụng cho bộ tạo số ngẫu nhiên là:

LCGEquation

Trong đó X (n + 1) là số tiếp theo, a là bội số, X (n) là số hiện tại, c là số gia và m là môđun.

Nếu bạn xem xét kỹ hơn Random, a, c và m được xác định trong tiêu đề của lớp

private static final long multiplier = 0x5DEECE66DL;   //= 25214903917 -- 'a'
private static final long addend = 0xBL;               //= 11          -- 'c'
private static final long mask = (1L << 48) - 1;       //= 2 ^ 48 - 1  -- 'm'

và nhìn vào phương pháp, protected int next(int bits)đây là phương trình được thực hiện

nextseed = (oldseed * multiplier + addend) & mask;
//X(n+1) =  (X(n)   *      a     +    c  ) mod m

Điều này ngụ ý rằng phương thức seedUniquifier()thực sự nhận được X (n) hoặc trong trường hợp đầu tiên khi khởi tạo X (0) 8682522807148012 * 181783497276652981, giá trị này thực sự được sửa đổi thêm bởi giá trị của System.nanoTime(). Thuật toán này phù hợp với phương trình ở trên nhưng với X (0) = 8682522807148012, a = 181783497276652981, m = 2 ^ 64 và c = 0. Nhưng khi mod m của được tạo trước bởi quá trình tràn dài, phương trình trên chỉ trở thành

eq2

Nhìn vào tờ giấy , giá trị của a = 1181783497276652981là của m = 2 ^ 64, c = 0. Vì vậy, nó dường như chỉ là lỗi đánh máy và giá trị 8682522807148012của X (0) dường như là một số được chọn ngẫu nhiên từ mã kế thừa cho Random. Như đã thấy ở đây. Nhưng công lao của những con số được chọn này vẫn có thể có giá trị nhưng như Thomas B. đã đề cập có lẽ không "tốt" như trong bài báo.

CHỈNH SỬA - Những suy nghĩ ban đầu dưới đây đã được làm rõ nên có thể bị bỏ qua nhưng hãy để nó tham khảo

Điều này dẫn tôi đến kết luận:

  1. Tham chiếu đến bài báo không phải cho bản thân giá trị mà cho các phương pháp được sử dụng để lấy các giá trị do các giá trị khác nhau của a, c và m

  2. Nó chỉ là sự trùng hợp ngẫu nhiên khi giá trị khác với giá trị đầu tiên là 1 và nhận xét bị đặt sai vị trí (mặc dù vẫn đang đấu tranh để tin vào điều này)

HOẶC LÀ

Đã có một sự hiểu lầm nghiêm trọng về các bảng trong bài báo và các nhà phát triển đã chỉ chọn một giá trị ngẫu nhiên vào thời điểm nó được nhân ra điểm trong việc sử dụng giá trị bảng ngay từ đầu, đặc biệt là khi bạn có thể cung cấp giá trị hạt giống của riêng mình theo bất kỳ cách nào trong trường hợp này các giá trị này thậm chí không được tính đến

để trả lời câu hỏi của bạn

Có thể các số khác đã được chọn sẽ hoạt động tốt như hai số này? Tại sao hoặc tại sao không?

Có, bất kỳ số nào có thể đã được sử dụng, trên thực tế, nếu bạn chỉ định một giá trị gốc khi bạn Khởi tạo ngẫu nhiên, bạn đang sử dụng bất kỳ giá trị nào khác. Giá trị này không có bất kỳ ảnh hưởng nào đến hiệu suất của máy phát điện, giá trị này được xác định bởi các giá trị a, c và m được mã hóa cứng trong lớp.


1
Không hẳn - Có hai thuật toán: (i) 1 để tạo một hạt ngẫu nhiên mới mỗi khi hàm tạo được gọi. Thuật ngữ đó sử dụng X_n + 1 = X_n * a đơn giản. Do tràn dài nên điều này tương đương với X_n + 1 = X_n * a mod m. Với a = 181783497276652981 và m = 2 ^ 64. (ii) Một thuật toán khác, bắt đầu từ một hạt giống nhất định, tạo ra một chuỗi số ngẫu nhiên. Thuật ngữ thứ hai đó là thuật ngữ bạn đề cập và các tài liệu giải thích rằng " Đây là bộ tạo số giả ngẫu nhiên đồng dư tuyến tính, như được mô tả bởi Knuth trong Nghệ thuật lập trình máy tính ".
assylias

1
@assylias Tôi hiểu quan điểm của bạn, bị cuốn vào mã nguồn Randomvà bài báo được trích dẫn, tôi đã hoàn toàn xem lại câu hỏi ban đầu, sẽ chỉnh sửa sớm, cảm ơn.
Java Devil

3

Theo liên kết bạn cung cấp, họ đã chọn ( sau khi thêm số 1 bị thiếu :) )) lợi nhuận tốt nhất từ ​​2 ^ 64 vì lâu không thể có số từ 2 ^ 128

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.