2
2

More than 3 years have passed since last update.

Javaで正規分布・対数正規分布の乱数を生成する

Last updated at Posted at 2021-01-02

はじめに

Javaで乱数を生成したい場合、まず思いつくのは Math.random() かと思います。
ただし、これで得られる値は一様分布であり、どの値もおなじ確率で現れるため、以下のようなダミー値を作りたい場合には適しません。

  • テストの点数…平均点が最も現れやすく、平均点から離れるほど現れにくくなる分布 → 正規分布
  • 世帯年収…グラフ中心から右側に長く広がる分布 → 対数正規分布

それぞれ頑張って計算式を書いても良いのですが(頑張って書いてしまったのですが)、ライブラリがあったのでそれを使いましょう。

正規分布

java.util.RandomnextGaussian を使います。

Random.html

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を使います。

LogNormalDistribution.html

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: 

参考

keisan.casio.jp 正規分布(グラフ)
keisan.casio.jp 対数正規分布(グラフ)

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2