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

JavaScriptでランダム文字列を生成する方法はいくつか有ります。

## 結論
以下はJavascriptで8文字のランダム文字列を生成するコードです。  
ほとんどの場合、これで十分です。

```js
// 生成する文字列の長さ
var l = 8;

// 生成する文字列に含める文字セット
var c = "abcdefghijklmnopqrstuvwxyz0123456789";

var cl = c.length;
var r = "";
for(var i=0; i<l; i++){
  r += c[Math.floor(Math.random()*cl)];
}
```

## いくつかの方法
ランダム文字列を生成する方法はいくつか有ります。

* toString()を使ったワンライナー

	```js
	Math.random().toString(36).slice(-8);
	```
* 1文字ずつピックアップしてく方法

	```js
	// 生成する文字列の長さ
	var l = 8;
	// 生成する文字列に含める文字セット
	var c = "abcdefghijklmnopqrstuvwxyz0123456789";
	var cl = c.length;
	var r = "";
	for(var i=0; i<l; i++){
		r += c[Math.floor(Math.random()*cl)];
	}
	```

* 上の方法の乱数生成に[seedrandom](https://github.com/davidbau/seedrandom)を使う方法

	seedrandomはMath.random()の挙動をイジって、seedを使って乱数を生成できるようにします。
	
	HTML
	
	```html
	<script src="//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.11/seedrandom.min.js"></script>
	<script>
	```
	
	JavaScript

	```js
	// かなりユニークなseedを生成し、以降のMath.random()のコール時にはそのseedを使って乱数を生成するようにする
	Math.seedrandom(); 
	/* 以降は既に上で紹介したのと全く同じコードでランダム文字列を生成すればよい */
	```

## 各方法の検証
上のそれぞれの方法がどれくらいユニークなランダム文字列を生成できるかを検証します。
で8文字のランダム文字列を生成した場合、どれくらいの割合で重複する文字列が生成されてしまうかを調べてみました。
調査は以下のコードを書いて行いました。
以下のコードをHTMLファイルとして保存してブラウザで開くとコンソールに結果が表示されます。

```html
<!DOCTYPE html>
<html>
<head>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.11/seedrandom.min.js"></script>
  <script>
    // seedrandomがオリジナルのMath.randomを上書きするので退避しておく
    var original_random = Math.random;

    // ワンライナーによる方法
    function method1(len) {
      return Math.random().toString(36).slice(-len);
    }

    // 1文字ずつピックアップする方法
    function method2(len) {
      var l = len; // 生成する文字列の長さ
      var c = "abcdefghijklmnopqrstuvwxyz0123456789"; // 生成する文字列に含める文字セット
      var cl = c.length;
      var r = "";
      for (var i = 0; i < l; i++) {
        r += c[Math.floor(Math.random() * cl)];
      }
      return r;
    }

    // 普通にワンライナーによる方法を実行
    function method1_without_seedrandom(len) {
      Math.random = original_random;
      return method1(len);
    }

    // 普通に1文字ずつピックアップする方法を実行
    function method2_without_seedrandom(len) {
      Math.random = original_random;
      return method2(len);
    }
    
    // seedrandomを使ってワンライナーによる方法を実行
    function method1_with_seedrandom(len) {
      Math.seedrandom();
      return method1(len);
    }

    // seedrandomを使って1文字ずつピックアップする方法を実行
    function method2_with_seedrandom(len) {
      Math.seedrandom();
      return method2(len);
    }

    // それぞれの方法で8文字のランダム文字列を最大100万回生成し、
    // 何回目で既出の文字列を生成してしまうかを調べる
    function check_uniqueness_of_each_method() {
      var methods = {
        method1_without_seedrandom: method1_without_seedrandom,
        method2_without_seedrandom: method2_without_seedrandom,
        method1_with_seedrandom: method1_with_seedrandom,
        method2_with_seedrandom: method2_with_seedrandom
      };
      var len = 8;
      var results = "";
      for (var method_name in methods) {
        var limit = 1000000;
        var strs = {};
        for (var i = 0; i < limit; i++) {
          var r = methods[method_name](len);
          if (strs[r]) {
            console.log(method_name + ' generated duplicate string at ' + i + 'th loop.');
            break;
          }
          strs[r] = true;
          if (i == limit - 1) {
            console.log(method_name + ' generated no duplicate string in ' + limit + ' loops.');
          }
        }
      }
    }
    check_uniqueness_of_each_method();
  </script>
</head>
<body></body>
</html>
```

## 結果
やる度に結果は変わるのですが概ね以下のことが言えます。

* ワンライナーの方法でやるとseedrandomの使用に関わらず高い頻度で重複した文字列を生成する
* 1文字ずつピックアップする方法でやるとseedrandomの使用に関わらず重複した文字列は生成されない
* seedrandomを使うとMath.random()の処理時間がかなり長くなる


## 結論
seedrandomを使わずに、1文字ずつピックアップする方法が重複のないランダム文字列を高速に生成できます。