JavaRush /Blog Java /Random-VI /Lý thuyết xác suất trong thực tế hay bạn có biết về Rando...
Viacheslav
Mức độ

Lý thuyết xác suất trong thực tế hay bạn có biết về Random

Xuất bản trong nhóm
Lý thuyết xác suất trong thực tế hay bạn biết về Random - 1

Giới thiệu

Trên thế giới có rất nhiều ngành khoa học nghiên cứu lý thuyết xác suất. Và khoa học bao gồm nhiều phần khác nhau. Ví dụ, trong toán học có một phần riêng dành cho việc nghiên cứu các sự kiện, số lượng ngẫu nhiên, v.v. Nhưng khoa học không được xem nhẹ. Trong trường hợp này, lý thuyết về xác suất bắt đầu hình thành khi mọi người cố gắng hiểu những mô hình ném xúc xắc khi chơi trò chơi may rủi. Nếu để ý kỹ, xung quanh chúng ta có rất nhiều thứ tưởng chừng như ngẫu nhiên. Nhưng mọi thứ ngẫu nhiên không hoàn toàn ngẫu nhiên. Nhưng nhiều hơn về điều này sau. Ngôn ngữ lập trình Java cũng hỗ trợ các số ngẫu nhiên, bắt đầu từ phiên bản đầu tiên của JDK. Các số ngẫu nhiên trong Java có thể được sử dụng bằng lớp java.util.Random . Để thử nghiệm, chúng tôi sẽ sử dụng trình biên dịch trực tuyến java tutorialspoint . Đây là một ví dụ cơ bản về việc sử dụng Ngẫu nhiên để mô phỏng việc ném “xúc xắc” hoặc hình khối bằng tiếng Nga:
import java.util.Random;

public class HelloWorld{
    public static void main(String []args){
        Random rnd = new Random();
        int number = rnd.nextInt(6) + 1;
        System.out.println("Random number: " + number);
    }
}
Có vẻ như đây có thể là phần kết thúc mô tả của Random , nhưng nó không đơn giản như vậy. Hãy mở mô tả của lớp java.util.Random trong API Java. Và ở đây chúng ta thấy những điều thú vị. Lớp Random sử dụng số giả ngẫu nhiên. Làm sao vậy? Hóa ra số ngẫu nhiên không phải ngẫu nhiên như vậy?
Lý thuyết xác suất trong thực tế hay bạn biết về Random - 2

Tính giả ngẫu nhiên java.util.Random

Tài liệu dành cho lớp java.util.Random nói rằng nếu các phiên bản Random được tạo với cùng tham số hạt giống và các chuỗi hành động giống nhau được thực hiện trên các phiên bản đó thì chúng sẽ trả về các chuỗi số giống hệt nhau. Và nếu nhìn kỹ hơn, chúng ta có thể thấy rằng Random thực sự có một hàm tạo lấy một số giá trị dài làm hạt giống:
Random rnd1 = new Random(1L);
Random rnd2 = new Random(1L);
boolean test = rnd1.nextInt(6) == rnd2.nextInt(6);
System.out.println("Test: " + test);
Ví dụ này sẽ trả về đúnghạt giống của cả hai trường hợp đều giống nhau. Phải làm gì? Hàm tạo mặc định giải quyết được một phần vấn đề. Dưới đây là một ví dụ về nội dung của hàm tạo ngẫu nhiên :
public Random() {
	this(seedUniquifier() ^ System.nanoTime());
}
Hàm tạo mặc định sử dụng phép toán OR độc quyền theo bit . Và sử dụng một long đại diện cho thời gian hiện tại và một số hạt giống cho việc này :
private static long seedUniquifier() {
	for (;;) {
		long current = seedUniquifier.get();
		long next = current * 181783497276652981L;
		if (seedUniquifier.compareAndSet(current, next))
			return next;
	}
}
Một điều thú vị khác ở đây là mỗi lệnh gọi phương thức getter seederUniquifier sẽ thay đổi giá trị của SeedUniquifier . Nghĩa là, lớp được thiết kế để chọn số ngẫu nhiên một cách hiệu quả nhất có thể. Tuy nhiên, như tài liệu nói, chúng " không an toàn về mặt mật mã ". Nghĩa là, đối với một số mục đích sử dụng cho mục đích mã hóa (tạo mật khẩu, v.v.), nó không phù hợp, bởi vì trình tự với cách tiếp cận thích hợp được dự đoán. Có những ví dụ về chủ đề này trên Internet, ví dụ ở đây: “ Dự đoán Math.random() tiếp theo trong Java ”. Hoặc ví dụ mã nguồn ở đây: " Vulnerability Weak Crypto ". java.util.Random (trình tạo số ngẫu nhiên) có một “phím tắt” nhất định, đó phiên bản rút gọn của lệnh gọi được thực hiện thông qua Math.random:
public static void main(String []args){
	int random_number = 1 + (int) (Math.random() * 6);
	System.out.println("Value: " + random_number);
}
Nhưng nếu bạn nhìn kỹ, Random sẽ nằm bên trong:
public static double random() {
	return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
	static final Random randomNumberGenerator = new Random();
}
JavaDoc khuyên bạn nên sử dụng lớp SecureRandom cho " trình tạo số giả ngẫu nhiên được bảo mật bằng mật mã ".
Lý thuyết xác suất trong thực tế hay bạn biết về Random - 3

Java ngẫu nhiên an toàn

Lớp SecureRandom là một lớp con của java.util.Random và nằm trong gói java.security . Bạn có thể đọc so sánh giữa hai lớp này trong bài viết " Sự khác biệt giữa java.util.Random và java.security.SecureRandom ". Tại sao SecureRandom này lại tốt như vậy? Thực tế là đối với anh ta, nguồn của các số ngẫu nhiên là một thứ nghe có vẻ kỳ diệu như “nhóm entropy cốt lõi”. Đây vừa là điểm cộng vừa là điểm trừ. Bạn có thể đọc về nhược điểm của việc này trong bài viết: “ Sự nguy hiểm của java.security.SecureRandom ”. Nói tóm lại, Linux có bộ tạo số ngẫu nhiên kernel (RNG). RNG tạo các số ngẫu nhiên dựa trên dữ liệu từ nhóm entropy, được điền dựa trên các sự kiện ngẫu nhiên trong hệ thống, chẳng hạn như thời gian của bàn phím và ổ đĩa, chuyển động của chuột, ngắt và lưu lượng mạng. Thông tin thêm về nhóm entropy được mô tả trong tài liệu " Số ngẫu nhiên trong Linux (RNG) hoặc cách "điền" /dev/random và /dev/urandom ". Trên hệ thống Windows, SHA1PRNG được sử dụng và triển khai trong sun.security.provider.SecureRandom. Với sự phát triển của Java, SecureRandom cũng đã thay đổi, điều này đáng đọc trong bài đánh giá “ Cập nhật Java SecureRandom kể từ tháng 4 năm 2016 ” để có bức tranh hoàn chỉnh.
Lý thuyết xác suất trong thực tế hay bạn đã biết về Random - 4

Đa luồng hoặc giống như Caesar

Nếu bạn nhìn vào mã của lớp Random , dường như không có gì cho thấy có sự cố. Các phương thức không được đánh dấu là đồng bộ hóa . Nhưng có một điều NHƯNG: khi tạo Random với hàm tạo mặc định trong một số luồng, chúng ta sẽ chia sẻ cùng một instance giữa chúng , qua đó Random sẽ được tạo . Và khi nhận được một số ngẫu nhiên mới, AtomicLong bên trong của cá thể cũng thay đổi . Một mặt, điều này không có gì sai về mặt logic, bởi vì... AtomicLong được sử dụng . Mặt khác, bạn phải trả tiền cho mọi thứ, kể cả năng suất. Và vì điều này nữa. Do đó, ngay cả tài liệu chính thức về java.util.Random cũng nói: " Các phiên bản của java.util.Random là các luồng an toàn. Tuy nhiên, việc sử dụng đồng thời cùng một phiên bản java.util.Random trên các luồng có thể gặp phải sự tranh chấp và hậu quả là hiệu suất kém. Hãy xem xét thay vào đó sử dụng ThreadLocalRandom trong các thiết kế đa luồng ". Nghĩa là, trong các ứng dụng đa luồng khi tích cực sử dụng Random từ nhiều luồng, tốt hơn nên sử dụng lớp ThreadLocalRandom . Cách sử dụng của nó hơi khác so với Random thông thường :
public static void main(String []args){
	int rand = ThreadLocalRandom.current().nextInt(1,7);
	System.out.println("Value: " + rand);
}
Như bạn có thể thấy, chúng tôi không chỉ định hạt giống cho nó . Ví dụ này được mô tả trong hướng dẫn chính thức của Oracle: Concurrent Random Numbers . Bạn có thể đọc thêm về lớp này trong bài đánh giá: " Guide to ThreadLocalRandom in Java ".
Lý thuyết xác suất trong thực tế hay bạn đã biết về Random - 5

StreamAPI và ngẫu nhiên

Với việc phát hành Java 8, chúng tôi có nhiều tính năng mới. Bao gồm API luồng. Và những thay đổi cũng ảnh hưởng đến việc tạo ra các giá trị Ngẫu nhiên . Ví dụ: lớp Random có ​​các phương thức mới cho phép bạn lấy Stream với các giá trị ngẫu nhiên như int, doublehoặc long. Ví dụ:
import java.util.Random;

public class HelloWorld{
    public static void main(String []args){
        new Random().ints(10, 1, 7).forEach(n -> System.out.println(n));
    }
}
Ngoài ra còn có một lớp mới SplittableRandom :
import java.util.SplittableRandom;

public class HelloWorld{
    public static void main(String []args){
        new SplittableRandom().ints(10, 1, 7).forEach(n -> System.out.println(n));
    }
}
Bạn có thể đọc thêm về sự khác biệt giữa SplittableRandom và các lớp khác tại đây: " Các cách khác nhau để tạo số ngẫu nhiên trong Java ".

Phần kết luận

Tôi nghĩ rằng nó đáng để đưa ra kết luận. Bạn cần đọc kỹ JavaDoc về các lớp được sử dụng. Đằng sau một thứ thoạt nhìn đơn giản như Random, có những sắc thái có thể tạo ra một trò đùa độc ác. #Viacheslav
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION