JavaRush /จาวาบล็อก /Random-TH /ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random
Viacheslav
ระดับ

ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random

เผยแพร่ในกลุ่ม
ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random - 1

การแนะนำ

มีวิทยาศาสตร์มากมายในโลกที่ศึกษาทฤษฎีความน่าจะเป็น และวิทยาศาสตร์ประกอบด้วยส่วนต่างๆ ตัวอย่างเช่น ในทางคณิตศาสตร์มีส่วนแยกต่างหากสำหรับการศึกษาเหตุการณ์สุ่ม ปริมาณ ฯลฯ แต่วิทยาศาสตร์ไม่ได้ถูกมองว่าเบา ในกรณีนี้ ทฤษฎีความน่าจะเป็นเริ่มเป็นรูปเป็นร่างเมื่อผู้คนพยายามทำความเข้าใจว่ารูปแบบการขว้างลูกเต๋าเมื่อเล่นเกมเสี่ยงโชคมีรูปแบบใดบ้าง หากมองใกล้ ๆ มีหลายสิ่งที่ดูเหมือนสุ่มอยู่รอบตัวเรา แต่การสุ่มทุกอย่างไม่ใช่การสุ่มโดยสมบูรณ์ แต่จะเพิ่มเติมในภายหลัง ภาษาการเขียนโปรแกรม Java ยังรองรับตัวเลขสุ่มโดยเริ่มจาก JDK เวอร์ชันแรก สามารถใช้ตัวเลขสุ่มใน Java ได้โดยใช้คลาสjava.util.Random สำหรับการทดสอบ เราจะใช้Tutorialspoint Java Online Compiler นี่คือตัวอย่างดั้งเดิมของการใช้Randomเพื่อจำลองการขว้าง "ลูกเต๋า" หรือลูกบาศก์ในภาษารัสเซีย:
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);
    }
}
ดูเหมือนว่านี่อาจเป็นจุดสิ้นสุดของคำอธิบายของRandomแต่มันไม่ง่ายขนาดนั้น เรามาเปิดคำอธิบายของ คลาส java.util.Randomใน Java API กัน และที่นี่เราเห็นสิ่งที่น่าสนใจ คลาสRandomใช้ตัวเลขสุ่มหลอก ยังไงล่ะ? ปรากฎว่าตัวเลขสุ่มไม่สุ่มขนาดนั้นเหรอ?
ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random - 2

การสุ่มหลอก java.util.Random

เอกสารประกอบสำหรับ คลาสjava.util.Randomระบุว่าหากอินสแตนซ์ของ Randomถูกสร้างขึ้นด้วย พารามิเตอร์ seed เดียวกัน และมีการดำเนินการตามลำดับเดียวกันบนอินสแตนซ์ อินสแตนซ์ก็จะส่งคืนลำดับตัวเลขที่เหมือนกัน และถ้าเรามองใกล้ ๆ เราจะเห็นว่าRandomมีConstructorที่ใช้ ค่า ยาวเป็นค่าSeed:
Random rnd1 = new Random(1L);
Random rnd2 = new Random(1L);
boolean test = rnd1.nextInt(6) == rnd2.nextInt(6);
System.out.println("Test: " + test);
ตัวอย่างนี้จะคืนค่าเป็นจริงเพราะ เมล็ดของทั้งสองกรณีจะเหมือนกัน จะทำอย่างไร? ตัวสร้างเริ่มต้นช่วยแก้ปัญหาได้บางส่วน ด้านล่างนี้เป็นตัวอย่างของเนื้อหาของRandom Constructor :
public Random() {
	this(seedUniquifier() ^ System.nanoTime());
}
ตัวสร้างเริ่มต้นใช้การดำเนินการแบบเอกสิทธิ์ระดับบิตหรือ และใช้ longแทนเวลาปัจจุบันและเมล็ด บางส่วน สำหรับสิ่งนี้:
private static long seedUniquifier() {
	for (;;) {
		long current = seedUniquifier.get();
		long next = current * 181783497276652981L;
		if (seedUniquifier.compareAndSet(current, next))
			return next;
	}
}
สิ่ง ที่น่าสนใจอีกอย่างหนึ่งก็คือ การเรียกใช้ เมธอด getter ของ seedUniquifier แต่ละครั้ง จะเปลี่ยนค่าของ seedUniquifier นั่นคือคลาสได้รับการออกแบบให้เลือกตัวเลขสุ่มอย่างมีประสิทธิภาพมากที่สุด อย่างไรก็ตาม ตามที่เอกสารระบุไว้ พวกเขา " ไม่ปลอดภัยด้วยการเข้ารหัส " นั่นคือเพื่อวัตถุประสงค์บางประการในการใช้งานเพื่อวัตถุประสงค์ในการเข้ารหัส (การสร้างรหัสผ่าน ฯลฯ ) มันไม่เหมาะเพราะ ทำนายลำดับด้วยแนวทางที่เหมาะสม มีตัวอย่างในหัวข้อนี้บนอินเทอร์เน็ต เช่น “ การทำนาย Math.random() ถัดไปใน Java ” หรือตัวอย่างซอร์สโค้ดที่นี่: " Vulnerability Weak Crypto " java.util.Random (ตัวสร้างตัวเลขสุ่ม) มี "ทางลัด" บางอย่าง นั่น คือเวอร์ชันย่อของการเรียกที่ดำเนินการผ่าน Math.random:
public static void main(String []args){
	int random_number = 1 + (int) (Math.random() * 6);
	System.out.println("Value: " + random_number);
}
แต่ถ้าคุณดูให้ดี Random เดียวกันก็อยู่ข้างใน:
public static double random() {
	return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
	static final Random randomNumberGenerator = new Random();
}
JavaDoc แนะนำให้ใช้ คลาส SecureRandomสำหรับ " ตัวสร้างตัวเลขสุ่มหลอกที่ปลอดภัยแบบเข้ารหัส "
ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random - 3

รักษาความปลอดภัย Java แบบสุ่ม

คลาสSecureRandomเป็นคลาสย่อยของjava.util.Randomและอยู่ในแพ็คเกจjava.security คุณสามารถอ่านการเปรียบเทียบของทั้งสองคลาสนี้ได้ในบทความ " ความแตกต่างระหว่าง java.util.Random และ java.security.SecureRandom " ทำไม SecureRandom ถึงดีขนาดนี้? ความจริงก็คือสำหรับเขาแล้ว แหล่งที่มาของตัวเลขสุ่มนั้นฟังดูมหัศจรรย์ราวกับ "แหล่งรวมเอนโทรปีหลัก" นี่เป็นทั้งบวกและลบ คุณสามารถอ่านเกี่ยวกับข้อเสียของสิ่งนี้ได้ในบทความ: “ อันตรายของ java.security.SecureRandom ” กล่าวโดยสรุป Linux มีตัวสร้างตัวเลขสุ่มเคอร์เนล (RNG) RNG สร้างตัวเลขสุ่มตามข้อมูลจากกลุ่มเอนโทรปี ซึ่งเติมตามเหตุการณ์สุ่มในระบบ เช่น การกำหนดเวลาของแป้นพิมพ์และดิสก์ การเคลื่อนไหวของเมาส์ การขัดจังหวะ และการรับส่งข้อมูลเครือข่าย ข้อมูลเพิ่มเติมเกี่ยวกับกลุ่มเอนโทรปีอธิบายไว้ในเนื้อหา " Random Numbers in Linux (RNG) หรือวิธี "เติม" /dev/random และ /dev/urandom " บนระบบ Windows จะใช้ SHA1PRNG และนำไปใช้ใน sun.security.provider.SecureRandom ด้วยการพัฒนาของ Java SecureRandom ก็เปลี่ยนไปเช่นกันซึ่งควรอ่านในการทบทวน“ การอัปเดต Java SecureRandom ณ เดือนเมษายน 2559 ” เพื่อให้ได้ภาพรวมที่สมบูรณ์
ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random - 4

มัลติเธรดหรือเป็นเหมือนซีซาร์

หากคุณดูโค้ดของ คลาส Randomดูเหมือนจะไม่มีอะไรบ่งบอกถึงปัญหา วิธีการไม่ถูกทำเครื่องหมายว่าซิงโครไนซ์ แต่มีอย่างหนึ่งคือ: เมื่อสร้างRandomด้วย Constructor เริ่มต้นในหลายเธรด เราจะแชร์seed เดียวกันระหว่างเธรดเหล่านั้น โดยที่Random จะ ถูกสร้างขึ้น และเมื่อได้รับหมายเลขสุ่มใหม่AtomicLong ภายใน ของอินสแตนซ์ก็จะเปลี่ยนไปด้วย ในแง่หนึ่ง ไม่มีอะไรผิดปกติกับเรื่องนี้จากมุมมองเชิงตรรกะ เพราะ... ใช้AtomicLong _ ในทางกลับกัน คุณต้องจ่ายทุกอย่าง รวมถึงประสิทธิภาพการทำงานด้วย และสำหรับเรื่องนี้ด้วย ดังนั้น แม้แต่เอกสารอย่างเป็นทางการสำหรับjava.util.Randomก็บอกว่า: " อินสแตนซ์ของ java.util.Random เป็นแบบ threadsafe อย่างไรก็ตาม การใช้อินสแตนซ์ java.util.Random เดียวกันพร้อมกันข้ามเธรดอาจพบข้อขัดแย้งและเป็นผลให้ประสิทธิภาพไม่ดี พิจารณา แทนที่จะใช้ ThreadLocalRandom ในการออกแบบแบบมัลติเธรด ". นั่นคือในแอปพลิเคชันแบบมัลติเธรดเมื่อใช้Randomจากหลายเธรดควรใช้ คลาส ThreadLocalRandomจะ ดีกว่า การใช้งานแตกต่างจากปกติเล็กน้อยRandom :
public static void main(String []args){
	int rand = ThreadLocalRandom.current().nextInt(1,7);
	System.out.println("Value: " + rand);
}
อย่างที่คุณเห็น เราไม่ได้ระบุเมล็ดพันธุ์ไว้ ตัวอย่าง นี้ได้อธิบายไว้ในบทช่วยสอนอย่างเป็นทางการจาก Oracle: Concurrent Random Numbers คุณสามารถอ่านเพิ่มเติมเกี่ยวกับคลาสนี้ได้ในการทบทวน: " Guide to ThreadLocalRandom in Java "
ทฤษฎีความน่าจะเป็นในทางปฏิบัติหรือคุณรู้เกี่ยวกับ Random - 5

StreamAPI และสุ่ม

ด้วยการเปิดตัว Java 8 เรามีคุณสมบัติใหม่มากมาย รวมถึงสตรีม API และการเปลี่ยนแปลงยังส่งผลต่อการสร้างค่าสุ่ม ด้วย ตัวอย่างเช่น คลาสRandom มีวิธีการ ใหม่ที่ให้คุณรับStreamที่มีค่าสุ่มเช่นintหรือ ตัวอย่างเช่น: doublelong
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));
    }
}
นอกจากนี้ยังมีคลาสใหม่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));
    }
}
คุณสามารถอ่าน เพิ่มเติมเกี่ยวกับความแตกต่างระหว่างSplittableRandomและคลาสอื่นๆ ได้ที่นี่: " วิธีต่างๆ ในการสร้างตัวเลขสุ่มใน Java "

บทสรุป

ฉันคิดว่ามันคุ้มค่าที่จะได้ข้อสรุป คุณต้องอ่าน JavaDoc อย่างละเอียดสำหรับคลาสที่ใช้ เบื้องหลังบางสิ่งที่เรียบง่ายตั้งแต่แรกเห็นอย่าง Random มีความแตกต่างที่สามารถเล่นเป็นเรื่องตลกที่โหดร้ายได้ #เวียเชสลาฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION