Skip to content

Instantly share code, notes, and snippets.

@yKimisaki
Last active July 9, 2020 12:05
Show Gist options
  • Save yKimisaki/f608a52cd4f11cf2150235df2dae775f to your computer and use it in GitHub Desktop.
Save yKimisaki/f608a52cd4f11cf2150235df2dae775f to your computer and use it in GitHub Desktop.
namespace Tonari.Linq
{
public static class ReadOnlyListExtensions
{
private static Random Random = new Random();
// あらかじめ1~必要数ぐらいの数列をキャッシュ
private const int LinedIntegersLength = 1024;
private static int[] LinedIntegersCache;
static ReadOnlyListExtensions()
{
LinedIntegersCache = new int[LinedIntegersLength];
for (int i = 0; i < LinedIntegersLength; i++) LinedIntegersCache[i] = i;
}
public static int[] CreateAndShuffleLinedIntegers(int length)
{
// 毎度生成じゃなくて、キャッシュから丸っとコピーして速度を稼ぐ
var values = new int[length];
Buffer.BlockCopy(LinedIntegersCache, 0, values, 0, length * sizeof(int));
void Swap(int[] array, int left, int right)
{
var temp = array[left];
array[left] = array[right];
array[right] = temp;
}
// 入れ替えずとも最後の1個は確定してるからその手前で抜ける
while (length > 2)
{
--length;
// ランダムに持ってきて最後と入れ替える
// 次のループでlengthが1減るので最後がランダムに判定に入らず確定
Swap(values, Random.Next(length), length);
}
return values;
}
public static IEnumerable<T> Shuffle<T>(this IReadOnlyList<T> source)
{
foreach (var index in CreateAndShuffleLinedIntegers(source.Count))
yield return source[index];
}
public static T SampleOne<T>(this IReadOnlyList<T> source)
{
return source[Random.Next(source.Count)];
}
public static T SampleOne<T>(this IReadOnlyList<T> source, Predicate<T> predicate)
{
foreach (var element in Shuffle(source))
{
if (predicate(element))
return element;
}
return default;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment