# 【Delphi】Fisher–Yates のシャッフル --- tags: Delphi programming Pascal objectpascal created_at: 2021-03-11 updated_at: 2021-03-11 --- # はじめに Fisher–Yates のシャッフルを Delphi で書いてみようと思います。 ※ Delphi 10.4 Sydney を使っていますが、10.3 Rio でも大丈夫です。 # コード アルゴリズムは `箱からランダムで値を取り出して別の場所に置くことを繰り返す` だけです。 - [フィッシャー–イェーツのシャッフル (Wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A3%E3%83%83%E3%82%B7%E3%83%A3%E3%83%BC%E2%80%93%E3%82%A4%E3%82%A7%E3%83%BC%E3%83%84%E3%81%AE%E3%82%B7%E3%83%A3%E3%83%83%E3%83%95%E3%83%AB) このシンプルなアルゴリズムに **「フィッシャー–イェーツのシャッフル (Fisher–Yates shuffle)」** という名前が付いている事を知ったのはそんなに昔ではありませんでした。ちょっとプログラムの経験がある人は名前は知らなくても、一度は同様の実装をした事があるんじゃないでしょうか? ## ShuffledIndex() 関数 シャッフルされた Integer 配列を返す関数です。 ```pascal function ShuffledIndex(const Count: Integer): TArray; begin SetLength(Result, Count); for var i := 0 to High(Result) do Result[i] := i; for var i := High(Result) downto 1 do begin var j := Random(Succ(i)); var t := Result[j]; Result[j] := Result[i]; Result[i] := t; end; end; { ShuffledIndex } ``` Delphi 7 や 2007 だと次のようになります。 ```pascal uses Types; function ShuffledIndex(const Count: Integer): TIntegerDynArray; var i, j, t: Integer; begin SetLength(Result, Count); for i := 0 to High(Result) do Result[i] := i; for i := High(Result) downto 1 do begin j := Random(Succ(i)); t := Result[j]; Result[j] := Result[i]; Result[i] := t; end; end; { ShuffledIndex } ``` 1. パラメータ Count の数で Integer 型の動的配列 (結果) を初期化。 2. 配列の先頭から順に `0..Count-1` の数値を格納。 3. 配列をシャッフル。 この関数さえあればいかなる型の配列でもシャッフルできます。アルゴリズムは簡単なのでその都度書いてもいいのですが、一つ関数を書いておくと便利ですよね。 ## 実行 次のような感じで使えます。例では **String** 型の配列をシャッフルしてコンソールに出力しています。 ```pascal:FisherYatesShuffle.dpr program FisherYatesShuffle; {$APPTYPE CONSOLE} function ShuffledIndex(const Count: Integer): TArray; ... end; { ShuffledIndex } begin Randomize; var a := ['ZERO', 'UNO', 'DUE', 'NANO', 'MICRO', 'PRO MINI', 'PRO MICRO', 'LEONARDO', 'MEGA2560']; for var i in ShuffledIndex(Length(a)) do Writeln(a[i]); end. ``` Delphi 2007 ではこうなります。 ```pascal:FisherYatesShuffle.dpr program FisherYatesShuffle; {$APPTYPE CONSOLE} uses Types; var a: TStringDynArray; i: Integer; function ShuffledIndex(const Count: Integer): TIntegerDynArray; ... end; { ShuffledIndex } begin Randomize; a := TStringDynArray.Create('ZERO', 'UNO', 'DUE', 'NANO', 'MICRO', 'PRO MINI', 'PRO MICRO', 'LEONARDO', 'MEGA2560'); for i in ShuffledIndex(Length(a)) do Writeln(a[i]); end. ``` Delphi 7 だとこうなります。 ```pascal:FisherYatesShuffle.dpr program FisherYatesShuffle; {$APPTYPE CONSOLE} uses Types; var a: TStringDynArray; i: Integer; ia: TIntegerDynArray; function ShuffledIndex(const Count: Integer): TIntegerDynArray; ... end; begin Randomize; SetLength(a, 9); a[0] := 'ZERO'; a[1] := 'UNO'; a[2] := 'DUE'; a[3] := 'NANO'; a[4] := 'MICRO'; a[5] := 'PRO MINI'; a[6] := 'PRO MICRO'; a[7] := 'LEONARDO'; a[8] := 'MEGA2560'; ia := ShuffledIndex(Length(a)); for i := 0 to High(ia) do Writeln(a[ia[i]]); end. ``` # おわりに あまり必要性を感じませんが、`ShuffledIndex()` 関数をコレクションにしても面白いかもですね...って、**"ホクホクのイモ" の時に書いとったわ!** - [ホクホクのイモを Delphi で書いてみる (Qiita)](./adef65fcb10c0144b3b0.md) ```pascal:FisherYatesShuffle.dpr program FisherYatesShuffle; {$APPTYPE CONSOLE} uses uPermutations; begin Randomize; var a := ['ZERO', 'UNO', 'DUE', 'NANO', 'MICRO', 'PRO MINI', 'PRO MICRO', 'LEONARDO', 'MEGA2560']; for var i in Perm(Length(a)) do Writeln(a[i]); end. ``` アラ、簡単。 **See also:** - [<6> 構造化型の概要と配列型 (標準 Pascal 範囲内での Delphi 入門) (Qiita)](./eedda6d38b6d0887d4ac.md)