Kebarangkalian di Jawa

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat beberapa contoh bagaimana kita dapat melaksanakan kebarangkalian dengan Java.

2. Meniru Kebarangkalian Asas

Untuk mensimulasikan kebarangkalian di Java, perkara pertama yang perlu kita lakukan adalah menjana nombor rawak. Nasib baik, Java memberi kita banyak penjana nombor rawak .

Dalam kes ini, kami akan menggunakan kelas SplittableRandom kerana ia memberikan rawak berkualiti tinggi dan agak cepat:

SplittableRandom random = new SplittableRandom();

Maka kita perlu menghasilkan nombor dalam julat dan membandingkannya dengan nombor lain yang dipilih dari julat itu. Setiap nombor dalam julat mempunyai peluang yang sama untuk ditarik. Oleh kerana kita mengetahui julatnya, kita mengetahui kebarangkalian menarik nombor yang kita pilih. Dengan cara itu kita mengawal kebarangkalian :

boolean probablyFalse = random.nextInt(10) == 0

Dalam contoh ini, kita memperoleh nombor dari 0 hingga 9. Oleh itu, kebarangkalian melukis 0 sama dengan 10%. Sekarang, mari dapatkan nombor rawak dan uji apakah nombor yang dipilih lebih rendah daripada yang dilukis:

boolean whoKnows = random.nextInt(1, 101) <= 50

Di sini, kita memperoleh nombor dari 1 hingga 100. Peluang untuk nombor rawak kita menjadi lebih kecil atau sama dengan 50 adalah tepat 50%.

3. Pembahagian Seragam

Nilai yang dihasilkan sehingga tahap ini jatuh ke dalam taburan seragam. Ini bermaksud bahawa setiap acara, misalnya menggulung sejumlah angka, mempunyai peluang yang sama untuk berlaku.

3.1. Memohon Fungsi Dengan Kebarangkalian yang Diberikan

Sekarang, katakan kita mahu melaksanakan tugas dari semasa ke semasa dan mengawal kebarangkaliannya. Sebagai contoh, kami mengendalikan laman web e-dagang dan kami ingin memberikan potongan harga kepada 10% pengguna kami.

Untuk melakukannya, mari kita laksanakan kaedah yang akan mengambil tiga parameter: pembekal untuk memanggil dalam beberapa peratusan kes, pembekal kedua untuk memanggil kes yang lain, dan kebarangkalian.

Pertama, kami menyatakan SplittableRandom kami sebagai Malas menggunakan Vavr. Dengan cara ini, kami akan menjadikannya hanya sekali, atas permintaan pertama:

private final Lazy random = Lazy.of(SplittableRandom::new); 

Kemudian, kami akan melaksanakan fungsi menguruskan kebarangkalian:

public  withProbability(Supplier positiveCase, Supplier negativeCase, int probability) { SplittableRandom random = this.random.get(); if (random.nextInt(1, 101) <= probability) { return positiveCase.get(); } else { return negativeCase.get(); } }

3.2. Kebarangkalian Persampelan Dengan Kaedah Monte Carlo

Mari kita membalikkan proses yang kita lihat di bahagian sebelumnya. Untuk melakukannya, kami akan mengukur kebarangkalian menggunakan kaedah Monte Carlo. Ia menghasilkan jumlah peristiwa rawak yang tinggi dan mengira berapa banyak yang memenuhi syarat yang disediakan. Ia berguna apabila kebarangkalian sukar atau tidak mungkin untuk dikira secara analitik.

Sebagai contoh, jika kita melihat dadu enam sisi, kita tahu bahawa kebarangkalian menggulung nombor tertentu adalah 1/6. Tetapi, jika kita mempunyai dadu misteri dengan sebilangan sisi yang tidak diketahui, sukar untuk mengetahui kemungkinannya. Daripada menganalisis dadu, kita hanya boleh menggulungnya berkali-kali dan mengira berapa kali peristiwa tertentu berlaku.

Mari lihat bagaimana kita dapat melaksanakan pendekatan ini. Pertama, kita akan cuba menghasilkan nombor 1 dengan kebarangkalian 10% untuk satu juta kali dan menghitungnya:

int numberOfSamples = 1_000_000; int probability = 10; int howManyTimesInvoked = Stream.generate(() -> randomInvoker.withProbability(() -> 1, () -> 0, probability)) .limit(numberOfSamples) .mapToInt(e -> e) .sum();

Kemudian, jumlah nombor yang dihasilkan dibahagi dengan jumlah sampel akan menjadi perkiraan kebarangkalian kejadian:

int monteCarloProbability = (howManyTimesInvoked * 100) / numberOfSamples; 

Ingatlah, kebarangkalian yang dikira dihampirkan. Semakin tinggi bilangan sampel, semakin baik penghampirannya.

4. Pengedaran Lain

Pengedaran seragam berfungsi dengan baik untuk memodelkan perkara seperti permainan. Agar permainan menjadi adil, semua acara mesti mempunyai kebarangkalian yang sama untuk berlaku.

Walau bagaimanapun, dalam kehidupan sebenar, pengedaran biasanya lebih rumit. Peluang tidak sama untuk berlainan perkara yang berlaku.

Sebagai contoh, terdapat sangat sedikit orang yang sangat pendek dan sangat sedikit yang sangat tinggi. Kebanyakan orang mempunyai tinggi rata-rata, yang bermaksud bahawa ketinggian orang mengikut taburan normal. Sekiranya kita perlu menghasilkan ketinggian manusia secara rawak, maka tidak mencukupi untuk menghasilkan bilangan kaki secara rawak.

Nasib baik, kita tidak perlu menerapkan model matematik yang mendasarinya. Kita perlu mengetahui sebaran mana yang akan digunakan dan bagaimana mengkonfigurasinya , misalnya, menggunakan data statistik.

Perpustakaan Apache Commons memberi kami implementasi untuk beberapa pengedaran. Mari laksanakan pengedaran normal dengannya:

private static final double MEAN_HEIGHT = 176.02; private static final double STANDARD_DEVIATION = 7.11; private static NormalDistribution distribution = new NormalDistribution(MEAN_HEIGHT, STANDARD_DEVIATION); 

Menggunakan API ini sangat mudah - kaedah sampel memperoleh nombor rawak dari pengedaran:

public static double generateNormalHeight() { return distribution.sample(); }

Akhirnya, mari kita membalikkan proses:

public static double probabilityOfHeightBetween(double heightLowerExclusive, double heightUpperInclusive) { return distribution.probability(heightLowerExclusive, heightUpperInclusive); }

Akibatnya, kita akan mendapat kebarangkalian seseorang mempunyai ketinggian antara dua batas. Dalam kes ini, ketinggian bawah dan atas.

5. Kesimpulan

Dalam artikel ini, kami belajar bagaimana menghasilkan peristiwa rawak dan bagaimana mengira kebarangkalian kejadian tersebut berlaku. Kami menggunakan taburan seragam dan normal untuk memodelkan situasi yang berbeza.

Contoh lengkap boleh didapati di GitHub.