てくメモ

trivial な notes

【C#】.NET 8 で Random クラスに追加された Shuffle と GetItems の中身の確認

.NET 8で追加されたRandom.ShuffleメソッドとRandom.GetItemsメソッドの中身を確認しておく。(.NET 8.0 リリース時点)

前者はシャッフル、後者は選択肢の中からランダムに選ぶ(よその名前では choice とか sample とか)もの。

なお、System.Security.Cryptography.RandomNumberGeneratorにも同様のメソッドが追加されている。



Shuffleメソッド

待望(?)の標準ライブラリシャッフル。
Random.Shuffle メソッド (System) | Microsoft Learn

Span<int> span = [1, 2, 3, 4, 5];
Random.Shared.Shuffle(span);

// List のオーバーロードはない
List<int> list = [1, 2, 3, 4, 5];
Random.Shared.Shuffle(CollectionsMarshal.AsSpan(list));


中身はフィッシャー・イェーツアルゴリズム。外部領域は利用しない。
Source Browser(リンク先:Shuffle(Span<T>)


新しく書くなら使わない理由はないはず。
OrderBy(_ => Guid.NewGuid())は姿を消すだろうか。消さないかな。

GetItemsメソッド

待望(?)の標準ライブラリチョイス。
Random.GetItems メソッド (System) | Microsoft Learn

destination を渡すものと length を渡すものがあるが、後者は中で配列をnewして destination としている。

ReadOnlySpan<int> choices = [1, 2, 3, 4, 5];

Span<int> zyuuren = stackalloc int[10];
Random.Shared.GetItems(choices, zyuuren);

int[] zyuurenArray = Random.Shared.GetItems(choices, 10);


中身は現在のところ、選択肢のReadOnlySpan<T> (choices) から destination に対し、ループしてchoices[Next(choices.Length)]を詰めていくだけ。
runtime/src/libraries/System.Private.CoreLib/src/System/Random.cs at 51cce4207b8484cac28aa1932ce797e9ea2ad929 · dotnet/runtime · GitHub


「現在のところ」というのは、特定条件での高速化がマージされているため。
Optimize Random{NumberGenerator}.GetItems for power-of-two choices by stephentoub · Pull Request #92229 · dotnet/runtime · GitHub
Source Browser(リンク先:GetItems(ReadOnlySpan<T>, Span<T>)

choices のLengthが2の累乗かつ256以下のとき、ループして都度NextするのではなくNextBytesから一気にチョイスするという感じ。

選択肢の数の話なので都合の良い数に設定できるかは場合によるけれど、上記条件のとおりにしておければ将来勝手に速くなる恩恵を受けることができる。