各言語での、本当に安全な乱数の作り方

## 結論

乱数生成と言っても、プログラムの世界では、
**ただの**疑似乱数生成関数と、**暗号論的**疑似乱数生成関数の2種類があります。

乱数を作るときは、どちらを使うべきか意識して使いましょう。

特に、

* 複数回高速実行されうる
* 並列同時実行されうる
* 暗号化の用途としてつかう

などの場合は、ただの**疑似乱数生成関数**ではなく、**暗号論的疑似乱数生成関数**を使いましょう。

## 疑似乱数？暗号論的？

(説明とかいいから、[さっさと言語別サンプル見せろという方はこちら](#各言語のサンプルコード)）

頭に**暗号論的**がつかない、ただの疑似乱数生成関数(PRNG)は、
高速または並列で、**同時に実行**されると
同じ値を返却してしまうという問題があります。

### 実験 

```C#:乱数生成サンプル(C#)
for (int i = 0; i < 10; i++)
{
    var r = new Random();
    Console.WriteLine(r.Next());
}
```

```console:出力結果
1319665291
1319665291
1319665291
1319665291
1319665291
1319665291
1319665291
1319665291
1319665291
1319665291
```

### 高速実行、または同時実行で同じ値が返却される理由

これが起きてしまうのは、ただの疑似乱数は

* シードが同じなら、同じ結果を返す
* シードを指定しないと、通常、現在時刻（ミリ秒単位）を使う（言語によっては微妙に異なるかも）

ためで、
つまり、高速 or 平行実行された結果、**同時**に実行されると、
同じ値を返却してしまうためである。

### 毎回異なるシードをあたえればいい？

単純に、ランダムな値がほしいなら、それでも大丈夫です。
が、高速でも並行でも他の実行と異なるシードってどう作ればいいのでしょう？

少なくともこれはダメです。

```C#:乱数を2回重ねる(C#)
for (int i = 0; i < 10; i++)
{
    var r = new Random();
    var r2 = new Random(r.Next());
    Console.WriteLine(r2.Next());
}
```

1個目の乱数の結果が同じになるので、
結局シードが同じになり、生成される乱数も同じになります。

幸運にも実行スレッド単位で一意のキーを持っている・取得できるなら、
そのキー＋実行回数をシードに与える事で、毎回異なる乱数を取得できますが、
そんな幸運は余りありませんし、そのためにスレッドの一意キーを生成するのは、ナンセンスです。

### 殺伐としたライブラリに暗号論的疑似乱数生成器(CSPRNG)が！

でも暗号論的疑似乱数生成器が実装されているなら、話は簡単です。

```C#:暗号論的疑似乱数生成サンプル(C#)
for (int i = 0; i < 10; i++)
{
    var randomByte = new byte[4];
    using(var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
    {
        rng.GetBytes(randomByte);
    }
    Console.WriteLine(System.BitConverter.ToInt32(randomByte,0));
}
```

```console:出力結果
-1078661811
510582217
-94865278
-602342842
-1315655704
-1868274994
-2112133054
-266973531
248470422
-1808682364
```

この通り、同時実行されても、それぞれ全く異なる乱数が手に入ります。

## 各言語のサンプルコード

以下、各言語でのただの～(PRNG)と暗号論的～(CSPRNG)のサンプルソースです。

※データ型は整数だったり、小数だったりバラバラです。:wink:

### __C#__

#### PRNG
```C#
var rng = new Random();
Console.WriteLine(rng.Next());
```
#### CSPRNG
```C#
var randomByte = new byte[4];
using(var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
    rng.GetBytes(randomByte);
}
Console.WriteLine(System.BitConverter.ToInt32(randomByte,0));
```

### __vb.net__
#### PRNG
```Vb
Dim rng As New Random()
Console.WriteLine(rng.Next())
```
#### CSPRNG
```vb
Dim randomByte As Byte() = New Byte(3) {}
Using rng As New System.Security.Cryptography.RNGCryptoServiceProvider()
    rng.GetBytes(randomByte)
End Using
Console.WriteLine(System.BitConverter.ToInt32(randomByte, 0))
```

### __PHP__

#### PRNG
```php
echo rand() 
```
#### CSPRNG
```php
echo random_int(PHP_INT_MIN,PHP_INT_MAX)
```

### __Java__

#### PRNG
```java
double randomNumber = Math.random();
System.out.println(randomNumber);
```

#### CSPRNG
```java
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;

public static void main (String args[]) {
    try {
        SecureRandom randomNumber = SecureRandom.getInstance("SHA1PRNG");
        System.out.println(randomNumber.nextDouble());
    } catch (NoSuchAlgorithmException nsae) { 
        // 例外ハンドラ
    }
}
```

### __Python__

#### PRNG
```python
import random

print(random.random())
```
#### CSPRNG
```python
import os
import sys
import random

rng = random.SystemRandom()
print(rng.randint(0, sys.maxsize))
```
### __node.js__

#### PRNG
```javascript
console.log(Math.random())
```
#### CSPRNG
標準では用意されていないが、[random-number-csprng](https://www.npmjs.com/package/random-number-csprng)を使えば大丈夫
以下は、掲載されていたサンプルソース

```javascript
var Promise = require("bluebird");
var randomNumber = require("random-number-csprng");
 
Promise.try(function() {
    return randomNumber(10, 30);
}).then(function(number) {
    console.log("Your random number:", number);
}).catch({code: "RandomGenerationError"}, function(err) {
    console.log("Something went wrong!");
});
```

### __javascript__

#### PRNG
```javascript
console.log(Math.random());
```

#### CSPRNG
```javascript
var array = new Uint32Array(1);
window.crypto.getRandomValues(array);
console.log(array[0]);
```
※なお、ブラウザによっては、`window.crypto.getRandomValues()`が使えない場合があります。
※その場合、適切な代替手段はありません。


### __SQL Server__
#### PRNG
```sql
SELECT RAND() 
```
#### CSPRNG
```sql
SELECT CAST(CRYPT_GEN_RANDOM(8000) AS INT)
```
### __MySql__
#### PRNG
```sql
SELECT RAND() 
```
#### CSPRNG
組み込み関数としては用意されていない。
アプリケーション側で作って下さい。

### __mongoDB__

#### PRNG
```mongoDB
print(Math.random())
```
#### CSPRNG
組み込み関数としては用意されていない（？）
アプリケーション側で作って下さい。