はじめに
Javaで乱数を生成したい場合、まず思いつくのは Math.random()
かと思います。
ただし、これで得られる値は一様分布であり、どの値もおなじ確率で現れるため、以下のようなダミー値を作りたい場合には適しません。
- テストの点数…平均点が最も現れやすく、平均点から離れるほど現れにくくなる分布 → 正規分布
- 世帯年収…グラフ中心から右側に長く広がる分布 → 対数正規分布
それぞれ頑張って計算式を書いても良いのですが(頑張って書いてしまったのですが)、ライブラリがあったのでそれを使いましょう。
正規分布
java.util.Random
の nextGaussian
を使います。
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class GaussianTest {
@Test
public void nextGaussianTest() {
Random random = new Random();
List<Double> results = IntStream.rangeClosed(1, 100000).boxed()
.map(i -> random.nextGaussian() * 20 + 50) // 標準偏差20、平均50
.collect(Collectors.toList());
printResults(results);
}
private void printResults(List<Double> results) {
for (int i = 0; i < 20; i++) {
int start = i * 5;
int end = (i + 1) * 5;
double ratio = results.stream().filter(val -> (val >= start && val < end)).count() / 100000d * 100;
System.out.println(start + "〜" + end + ": " + IntStream.rangeClosed(1, (int) ratio).mapToObj(val -> "■").collect(Collectors.joining("")));
}
}
}
こんな感じの分布になります。
0〜5:
5〜10: ■
10〜15: ■
15〜20: ■■
20〜25: ■■■
25〜30: ■■■■■
30〜35: ■■■■■■
35〜40: ■■■■■■■■
40〜45: ■■■■■■■■■
45〜50: ■■■■■■■■■
50〜55: ■■■■■■■■■
55〜60: ■■■■■■■■■
60〜65: ■■■■■■■■
65〜70: ■■■■■■
70〜75: ■■■■■
75〜80: ■■■
80〜85: ■■
85〜90: ■
90〜95: ■
95〜100:
# (※軸を揃えるためにスペースの数を調整してます)
対数正規分布
Apache CommonsのLogNormalDistribution
を使います。
import org.apache.commons.math3.distribution.LogNormalDistribution;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class GaussianTest {
@Test
public void logNormalDistributionTest() {
double MU = 3.0; // ln(x)の平均μ 大きいほどグラフの右側が伸びる
double SIGMA = 1.0; // ln(x)の標準偏差σ 大きいほどグラフが横に広がる
LogNormalDistribution distribution = new LogNormalDistribution(MU, SIGMA);
List<Double> results = IntStream.rangeClosed(1, 100000).boxed()
.map(i -> distribution.sample())
.collect(Collectors.toList());
printResults(results);
}
private void printResults(List<Double> results) {
// 略
}
}
結果はこうなりました。
0〜5: ■■■■■■■■
5〜10: ■■■■■■■■■■■■■■■■
10〜15: ■■■■■■■■■■■■■■
15〜20: ■■■■■■■■■■■
20〜25: ■■■■■■■■
25〜30: ■■■■■■
30〜35: ■■■■■
35〜40: ■■■■
40〜45: ■■■
45〜50: ■■
50〜55: ■■
55〜60: ■■
60〜65: ■
65〜70: ■
70〜75: ■
75〜80: ■
80〜85:
85〜90:
90〜95:
95〜100: