Zipf 分布な乱数を作るジェネレータを作ってみた。
車輪の再発明なのは重々承知。
たぶんもっとマシなライブラリがどこかにあるだろうけど、頭使わずに使えそうな単機能なのがググってもサクッとでないので作ってみた。
てきとーに作ったので精度は不明。使うときはそこらを十分検討してね。
public class ZipfRandom { private double[] cdf = null; private Random rnd = null; // m 要素数 // k 偏り具合パラメータ Zipf分布の 1/(n^k) の k // rnd 一様乱数生成器を入れる public ZipfRandom(int m, double k, Random rnd) { if (m <= 0) throw new IllegalArgumentException("m にはプラス値を入れれ"); if (rnd == null) throw new IllegalArgumentException("rnd に null はいらんのですよ"); this.rnd = rnd; cdf = new double[m]; // 正規化用の分母 double C = 0.0; for (int i=0;i<m;i++) { C += 1.0/Math.pow(i+1, k); } // 確率関数の積算を配列に作成 double sum = 0.0; for (int i=0;i<m;i++) { sum += 1.0/(Math.pow(i+1, k)*C); cdf[i] = sum; } } // [0, m-1] の範囲で zipf 分布に沿った乱数が返る public int getNext() { double r = rnd.nextDouble(); for (int i=0; i < cdf.length; i++) { if (r < cdf[i]) return i; } // ここに来たら駄目だろう。常識的に考えて。 throw new IllegalStateException(); } }
使い方。
public void main(String[] arg) { int eles = 10; // 要素数 int[] histgram = new int[eles]; // ヒストグラム入れ // 要素数とパラメータと乱数生成器を渡して Zipf 生成器を作成 ZipfRandom rnd = new ZipfRandom(eles, 1.0, new MersenneTwister()); // 10,000,000 個乱数を取得してヒストグラムを作る for (int i = 0; i < 10000000; i++) { histgram[rnd.getNext()]++; } // 結果表示 for (int e : histgram) { System.out.println(e); } }
結果はこんな感じ。
3410155 1707631 1138103 854965 684191 569974 487507 426716 379426 341332
だいたい 1, 1/2, 1/3, 1/4 ... 1/10 になってるから OK っぽい?
ライセンス
コピペするなりなんなりご自由に
バグで誤動作したり実験結果とかが変なことになってても責任持ちません
コメント歓迎いたします
memo
※再投稿しました。