[Delphi] 乱数マネージャの作成

# はじめに
[９日](https://qiita.com/pik/items/c3530f222a99365b32d2)、[１１日](https://qiita.com/pik/items/7b224f3cd9f7c3603e9c)、[１２日](https://qiita.com/pik/items/15655242d2bbdbc8d120)と３回に渡って乱数の紹介をしてきました。
ここからが本番です。

# 乱数マネージャの作成
過去３回の開設で、乱数には得手不得手がある事がわかりました。

|アルゴリズム|メリット|デメリット|
|---|---|---|
|線形合同法|実装が簡単|周期が短い|
|CNG Random|暗号用途に使用可能|Windows API なので他ではつかえない|
|XorShift|実装が簡単・高速|とくになし|
|Mersenne Twister|周期がとてつもなく長い|計算資源をそれなりに使う|

得手不得手に応じて、これらを簡単に切り替えられたら良いですよね。

[TBitmapCodecManager](https://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.Graphics.TBitmapCodecManager) のように、上記４つの乱数を切り替えられる、TRandomManager を作りましょう。

# TRandomManager 

TRandomManager の機能は

* 乱数アルゴリズムの登録
* 乱数アルゴリズムの呼び出し

とします。
実際の乱数は Random を呼んで実行するようにします。
（TBitmapCodecManager がコーデックを管理して、実際の使用は TBitmap を使う様な関係です）

## 登録機構を作る

乱数アルゴリズムは class として実装します。
（前回までは record で実装しました）

そのため、乱数アルゴリズムクラスを生成する機構が必要です。
そこで、乱数アルゴリズムクラスを生成する IRandomAlgoBuilder インターフェースを定義します。

```delphi
  IRandomAlgoBuilder = interface
  ['{4461DD5C-6BCF-4F3E-A93D-24ED90E157BB}']
    function CreateAlgo: IRandomAlgo;
  end;
```

この IRandomAlgoBuilder を TRandomManager に登録できるようにします。
必要になったら IRandomAlgoBuilder を呼び出して利用する乱数アルゴリズム本体を生成する仕組みです。

```delphi:Builder を登録するメソッド
class procedure RegisterAlgo(
  const AName: String;               // 乱数アルゴリズムの名前
  const ABuilder: IRandomAlgoBuilder // 乱数アルゴリズムを生成するビルダー
);
```

TXorShiftAlgoBuilder を登録する場合は↓のようになります。

```delphi:使用例
initialization
  TRandomManager.RegisterAlgo(TXorShiftAlgo.NAME, TXorShiftAlgoBuilder.Create);
```

## IRandomAlgo の定義

各アルゴリズムが実装しなければいけないインターフェース IRandomAlgo を作成します。
IRandomAlgoBuilder.CreateAlgo はこれを実装したクラスを生成して返します。

```delphi
  IRandomAlgo = interface
  ['{6508CC6E-67CD-4F63-B4FF-5F6461F2FE76}']
    procedure Initialize;
    procedure SetSeed(const ASeed: UInt64);
    function Execute: UInt32;
    function GetName: String;
    property Name: String read GetName;
  end;
```

全ての乱数クラスが IRandomAlgo を実装します。
そして、そのたびに ``` class(TInterfacedObject, IRandomAlgo)``` と書くのが面倒なので、基底クラスを作りました。
IRandomAlgo のメソッドは全て abstract で定義されています。

```delphi
  TRandomAlgo = class(TInterfacedObject, IRandomAlgo)
  protected
    procedure Initialize; virtual; abstract;
    procedure SetSeed(const ASeed: UInt64); virtual; abstract;
    function Execute: UInt32; virtual; abstract;
    function GetName: String; virtual; abstract;
  public
    property Name: String read GetName;
  end;
```

これも TXorShiftAlgo を例に説明します。

```delphi
  TXorShiftAlgo = class(TRandomAlgo)
  private const
    NAME = 'XorShift';
  private var
    FA: UInt32;
  private
    procedure Initialize; override;
    procedure SetSeed(const ASeed: UInt64); override;
    function Execute: UInt32; override;
    function GetName: String; override;
  end;
```

TRandomAlgo クラスを継承してメソッドを実装しています。

```delphi
function TXorShiftAlgo.Execute: UInt32;
begin
  FA := FA xor (FA shl 13);
  FA := FA xor (FA shr 17);
  FA := FA xor (FA shl 5);
  Result := FA;
end;

function TXorShiftAlgo.GetName: String;
begin
  Result := NAME;
end;

procedure TXorShiftAlgo.Initialize;
begin
  FA := Round(Frac(Now) * 24 * 60 * 60 * 1000) mod 1000;;
end;

procedure TXorShiftAlgo.SetSeed(const ASeed: UInt64);
begin
  DefaultRandomize(ASeed);
  FA := RandSeed;
end;
```

# 利用方法

最後に利用方法です。
PK.Math.Random.RandomManager を uses して各アルゴリズムを表す文字列でアルゴリズムを指定し、Random や Randomize を呼ぶだけです。

```delphi
uses
  PK.Math.Random.RandomManager;

procedure Sample;
begin
  TRandomManager.Current.SetAlgo('XorShift');

  Randomize;
  Writeln(Random(10));
end;
```

今まで紹介してきたアルゴリズムが使えます。

|アルゴリズム|文字列|Unit|
|---|---|---|
|線形合同法|LCG|PK.Math.Random.LCG.pas|
|CNG Random|CNG|PK.Math.Random.CNG.pas|
|XorShift|XorShift|PK.Math.Random.XorShift.pas|
|MersenneTwister|MT|PK.Math.Random.MersenneTwister.pas|

# ソース
下記からダウンロードできます。
https://github.com/freeonterminate/RandomManager

# まとめ
内部に全アルゴリズムを内包しているので、ちょっと効率が悪いですね…
何か良い方法はないかなと思った物の時間切れ…