Java で、ランダムな文字列を生成する方法

ちょっと学んだので整理。
まずJavaにはランダムを提供するクラスとして

  • java.util.Random
  • java.security.SecureRandom

の2つがあってRandomクラスは以下のような強度しか持っていない。

Random クラスのインスタンスは、一連の擬似乱数を生成します。クラスでは 48 ビットのシードを使い、このシードは線形合同法で変更されます。

http://sdc.sun.co.jp/java/docs/j2se/1.4/ja/docs/ja/api/java/util/Random.html

しかも初期化があまりよくなくて初回の乱数がとても偏る。

        Random r = new java.util.Random();
        double x, min = 1, max = 0;
        for (int i = 0; i < 1024; i++) {
            r.setSeed(i);
            x = r.nextDouble();
            if (x > max)
                max = x;
            if (x < min)
                min = x;
        }
        System.out.println("Min=" + min + " Max=" + max);
Min=0.6753377750582709 Max=0.7669794809891215

こんなプログラムを回してみるとと1000通りの種を渡しても初回生成値が極度に偏ってしまう。

んで、SecureRandomはこのあたりの問題を解消するのでててきた。

        for (int i = 0; i < 1024; i++) {
            sr.setSeed(i);
            x = sr.nextDouble();
            if (x > max){
                max = x;
            }
            if (x < min){
                min = x;
            }
        }
        BigDecimal bigMin = new BigDecimal(min).setScale(16,BigDecimal.ROUND_HALF_UP);
        System.out.println("Min=" + bigMin + " Max=" + max);
        x = 0.0;
Min=0.0004351500205996 Max=0.9986417236968099

SecureRandomにすると一気に分散するようになった。


ランダムな文字列を生成するには

そもそもランダムな文字列を作る為にRandomを久々にみてたわけですが、
今普通にランダムな文字列を使うのならcommons-langのRandomStringUtilsで事足りる。

アスキー文字なら

RandomStringUtils.randomAscii(10)
⇒DF|M1!7W=@

アルファベットなら

RandomStringUtils.randomAlphabetic(10)
⇒doKntQIMve

数字なら(0始まりも生成されてしまうので注意)

RandomStringUtils.randomNumeric(10)
⇒8266471697
頭0を嫌うなら
RandomStringUtils.random(1,"123456789") + RandomStringUtils.randomNumeric(9);

自分で指定した文字の中からランダムにしたいのなら

RandomStringUtils.random(10,"012345abcdef");
⇒c021cc0b24

長さもランダムにしたいのならRandomUtils.nextIntと組み合わせて(1〜3文字のランダムな文字が変える)

RandomStringUtils.random(RandomUtils.nextInt(3)+1,"12345678");
⇒1
⇒255
⇒86

まあつまりコモンズはすごいよねっていうエントリーです><

良い乱数・悪い乱数
commons-langでランダムな値を生成する--commons-langの便利メソッド - builder by ZDNet Japan