Skip to content

Instantly share code, notes, and snippets.

@htsign
Last active November 9, 2025 05:08
Show Gist options
  • Select an option

  • Save htsign/d5c28287735ad31d82730edb1fc726d5 to your computer and use it in GitHub Desktop.

Select an option

Save htsign/d5c28287735ad31d82730edb1fc726d5 to your computer and use it in GitHub Desktop.
標準偏差を求めるメソッドを生やしました。なるべく汎用的に、高速に動作するよう作ったつもりですが実測はしていないので微妙。
public static class MyMath
{
public interface ITypeConverter<T, TResult>
{
TResult Convert(T value);
}
/// <summary><c>TResult.CreateChecked</c> を用いて型変換を行います。</summary>
public readonly struct CheckedTypeConverter<T, TResult> : ITypeConverter<T, TResult>
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TResult Convert(T value) => TResult.CreateChecked(value);
}
/// <summary><c>TResult.CreateSaturating</c> を用いて型変換を行います。</summary>
public readonly struct SaturatingTypeConverter<T, TResult> : ITypeConverter<T, TResult>
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TResult Convert(T value) => TResult.CreateSaturating(value);
}
/// <summary><c>TResult.CreateTruncating</c> を用いて型変換を行います。</summary>
public readonly struct TruncatingTypeConverter<T, TResult> : ITypeConverter<T, TResult>
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TResult Convert(T value) => TResult.CreateTruncating(value);
}
/// <summary>標準偏差の分散方式</summary>
public enum VarianceType
{
/// <summary>母集団分散</summary>
Population,
/// <summary>サンプル分散</summary>
Sample,
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult>(this ReadOnlySpan<T> source, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
var conv = new TruncatingTypeConverter<T, TResult>();
return Stdev<T, TResult, TruncatingTypeConverter<T, TResult>>(source, conv, mode);
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult>(this Span<T> source, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
return Stdev<T, TResult>((ReadOnlySpan<T>)source, mode);
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult>(this ReadOnlyMemory<T> source, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
return Stdev<T, TResult>(source.Span, mode);
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult>(this Memory<T> source, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
return Stdev<T, TResult>(source.Span, mode);
}
/// <summary>標準偏差を指定した型変換を用いて計算します。</summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="conv"><c>T ⇒ TResult</c> の型変換戦略を内包するインターフェース</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult, TConv>(this ReadOnlySpan<T> source, TConv conv, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
where TConv : struct, ITypeConverter<T, TResult>
{
if (source.IsEmpty) throw new InvalidOperationException($"Source sequence is empty");
TResult count, sum;
count = sum = TResult.Zero;
foreach (T value in source)
{
count += TResult.One;
sum += conv.Convert(value);
}
TResult average = sum / count;
TResult sumSqr = TResult.Zero;
foreach (T value in source)
{
TResult deviation = conv.Convert(value) - average;
sumSqr += deviation * deviation;
}
TResult denom = (mode == VarianceType.Sample && TResult.One < count)
? count - TResult.One
: count;
return TResult.Sqrt(sumSqr / denom);
}
/// <summary>標準偏差を指定した型変換を用いて計算します。</summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="conv"><c>T ⇒ TResult</c> の型変換戦略を内包するインターフェース</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult, TConv>(this Span<T> source, TConv conv, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
where TConv : struct, ITypeConverter<T, TResult>
{
return Stdev<T, TResult, TConv>((ReadOnlySpan<T>)source, conv, mode);
}
/// <summary>標準偏差を指定した型変換を用いて計算します。</summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="conv"><c>T ⇒ TResult</c> の型変換戦略を内包するインターフェース</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult, TConv>(this ReadOnlyMemory<T> source, TConv conv, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
where TConv : struct, ITypeConverter<T, TResult>
{
return Stdev<T, TResult, TConv>(source.Span, conv, mode);
}
/// <summary>標準偏差を指定した型変換を用いて計算します。</summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="conv"><c>T ⇒ TResult</c> の型変換戦略を内包するインターフェース</param>
/// <param name="mode">標準偏差の分散方式</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TResult Stdev<T, TResult, TConv>(this Memory<T> source, TConv conv, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
where TConv : struct, ITypeConverter<T, TResult>
{
return Stdev<T, TResult, TConv>(source.Span, conv, mode);
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="mode">標準偏差の分散方式</param>
public static TResult Stdev<T, TResult>(this IEnumerable<T> source, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
{
var conv = new TruncatingTypeConverter<T, TResult>();
return Stdev<T, TResult, TruncatingTypeConverter<T, TResult>>(source, conv, mode);
}
/// <summary>
/// 標準偏差を計算します。
/// <c>T ⇒ TResult</c> の変換は <c>TResult.CreateTruncating</c> によって行われます。
/// </summary>
/// <param name="source">計算する対象の値の集合</param>
/// <param name="conv"><c>T ⇒ TResult</c> の型変換戦略を内包するインターフェース</param>
/// <param name="mode">標準偏差の分散方式</param>
public static TResult Stdev<T, TResult, TConv>(this IEnumerable<T> source, TConv conv, VarianceType mode = VarianceType.Population)
where T : INumberBase<T>
where TResult : INumber<TResult>, IRootFunctions<TResult>
where TConv : struct, ITypeConverter<T, TResult>
{
return source switch
{
List<T> list => Stdev<T, TResult, TConv>(CollectionsMarshal.AsSpan(list), conv, mode),
T[] array => Stdev<T, TResult, TConv>(array, conv, mode),
_ => Stdev<T, TResult, TConv>(source.ToArray(), conv, mode),
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment