今日も適当ダイアリー

PHP や Javascript や Symfony、BEAR.Sunday などのWeb周りのことを中心に。それ以外のことも気まぐれに投稿します。

パスワード(ランダム)文字列を生成してみる

パスワード自動生成のための javascript を書いてみた。

要件

要件は、下記の通りとします。

  • 生成する文字列の文字数を指定できること
  • 生成する文字列に使用する文字リストを指定できること
  • 生成する文字列の文字が重複しないようにするか否かを指定できること

関数定義

関数の定義は以下の通り

パスワード文字列(ランダム文字列)を生成する

@param     {Number}   i 生成する文字列の文字数
@param     {String}   s 生成する文字列に使用する文字リスト
@param     {Boolean}  f 文字が重複しないようにするか否か
@returns   {String}   生成された文字列

素直にforループでまわしてみる

単純に、forループを使ってランダムな文字列を生成してみます。

なお、今回は、一回限り使えればよかったので無名関数で定義してます。(なので、使いきりです)

(function(i,s,f) {
    var p = '';

    for (; i > 0; i--) {
        // 文字リストからランダムに選択
        var j = Math.floor(Math.random() * s.length);

        // 結果文字列に追加
        p += s.charAt(j);

        // 文字を重複させない場合は今回使った文字をリストから削除
        if (f) {
            s = s.substr(0, j) + s.substr(j + 1);
        }
    }

    return p;
})(
    10, // 文字数
    'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789', // 文字リスト
    true); // 文字を重複させない場合はtrue

念のため言っておくと、実際に利用するには、下記のように記述します。

document.getElementById('ターゲットのID').value =
    /* ここに本体のコード */

やっている事は単純で、forループ内で文字リストからランダムな一文字を選択して、それを結果文字列に追加しているだけです。
文字を重複させない場合は、文字リストから今使った文字を削除していく感じですね。

実行例:http://www.weekendphp.com/misc/blog/hatena/ja9/2010-09-01.1.html

圧縮してみる

上記のコードをあーだこーだして、改行やスペースなどを取っ払って、圧縮(?)してみます。

(function(i,s,f){for(var m=Math,p='';i>0;i--){var j=m.floor(m.random()*s.length);p+=s.charAt(j);s=f?s.substr(0,j)+s.substr(j+1):s}return p})(10,'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789',true);

本体部分は140バイトになりました。
もっと短く出来ないかと、別の方法も考えてみます。

こんどは再帰関数にしてみる

再帰関数にすれば、もっと短くなるのでは?と思い、チャレンジしてみます。

今回は、勉強がてら無名関数を再帰してみます。
自分自身は、arguments.calleeで参照できます。

参考:arguments.callee - JavaScript | MDN

(function(i,s,f) {
    var j = Math.floor(Math.random() * s.length); // 文字リストからランダムに選択

    if (i <= 1) {
        return s.charAt(j);
    }

        // 文字を重複させない場合は今回使った文字をリストから削除
    if (f) {
        s = s.substr(0, j) + s.substr(j + 1);
    }

    // i > 1 なら再帰させる
    return s.charAt(j) +
            arguments.callee(i - 1, s, f); // 自分自身を呼び出す
})(
    10, // 文字数
    'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789', // 文字リスト
    true); // 文字を重複させない場合はtrue

やっている事自体は、forの時と大差ありません。

圧縮してみる

あーだこーだします。

(function(i,s,f){var m=Math,j=m.floor(m.random()*s.length);return s.charAt(j)+(i>1?arguments.callee(i-1,f?s.substr(0,j)+s.substr(j+1):s,f):'')})(10,'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789',true);

どうかな?
と思ったら、本体部分は144バイトでした。
forループより4バイト増えてしまいました。

そもそも、arguments.callee という変数/メソッド名が長いので、不利だったぽいです。

また、時間が時間があったら別の方法も考えてみようかな。