Hãy để tôi xin lỗi trước, bởi vì điều này hơi khó hiểu ...
Trước hết, bạn đã biết rằng java.util.Random
hoàn toàn không phải là ngẫu nhiên. Nó tạo ra các chuỗi theo một cách hoàn toàn có thể dự đoán được từ hạt giống. Bạn hoàn toàn chính xác rằng, vì hạt giống chỉ dài 64 bit, nên nó chỉ có thể tạo ra 2 ^ 64 chuỗi khác nhau. Nếu bạn bằng cách nào đó tạo ra 64 bit ngẫu nhiên thực và sử dụng chúng để chọn một hạt giống, bạn không thể sử dụng hạt giống đó để chọn ngẫu nhiên giữa tất cả 52! trình tự có thể với xác suất bằng nhau.
Tuy nhiên, thực tế này không có kết quả, miễn là bạn không thực sự tạo ra nhiều hơn 2 ^ 64 chuỗi, miễn là không có gì 'đặc biệt' hoặc 'đặc biệt đáng chú ý' về chuỗi 2 ^ 64 mà nó có thể tạo ra .
Hãy nói rằng bạn có PRNG tốt hơn nhiều, sử dụng hạt 1000 bit. Hãy tưởng tượng bạn có hai cách để khởi tạo nó - một cách sẽ khởi tạo nó bằng cách sử dụng toàn bộ hạt giống và một cách sẽ băm hạt giống xuống 64 bit trước khi khởi tạo nó.
Nếu bạn không biết trình khởi tạo nào, bạn có thể viết bất kỳ loại thử nghiệm nào để phân biệt chúng không? Trừ khi bạn (chưa) đủ may mắn để kết thúc việc khởi tạo cái xấu với cùng 64 bit hai lần, thì câu trả lời là không. Bạn không thể phân biệt giữa hai trình khởi tạo mà không có một số kiến thức chi tiết về một số điểm yếu trong việc triển khai PRNG cụ thể.
Ngoài ra, hãy tưởng tượng rằng Random
lớp có một mảng gồm 2 ^ 64 chuỗi được chọn hoàn toàn và ngẫu nhiên tại một thời điểm trong quá khứ xa xôi và rằng hạt giống chỉ là một chỉ mục trong mảng này.
Vì vậy, việc Random
chỉ sử dụng 64 bit cho hạt giống của nó thực sự không nhất thiết là một vấn đề thống kê, miễn là không có khả năng đáng kể rằng bạn sẽ sử dụng cùng một hạt giống hai lần.
Tất nhiên, đối với các mục đích mã hóa , một hạt giống 64 bit là không đủ, bởi vì việc một hệ thống sử dụng cùng một hạt giống hai lần là khả thi về mặt tính toán.
BIÊN TẬP:
Tôi nên thêm rằng, mặc dù tất cả những điều trên là chính xác, rằng việc thực hiện thực tế java.util.Random
không phải là tuyệt vời. Nếu bạn đang viết một trò chơi bài, có thể sử dụng MessageDigest
API để tạo hàm băm SHA-256 "MyGameName"+System.currentTimeMillis()
và sử dụng các bit đó để xáo trộn bộ bài. Theo lập luận trên, miễn là người dùng của bạn không thực sự đánh bạc, bạn không phải lo lắng currentTimeMillis
về việc trả lại lâu. Nếu người dùng của bạn đang thực sự đánh bạc, sau đó sử dụng SecureRandom
không có hạt.
Random
không bao giờ là số ngẫu nhiên thực sự . Đó là một PRNG, trong đó P là viết tắt của "giả". Đối với các số ngẫu nhiên thực , bạn cần một nguồn ngẫu nhiên (chẳng hạn như Random.org).