かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

方法: LINQ クエリのカスタム メソッドを追加する??

ノイエ先生が突然きれてたので、試してみました。前半がMSDNからのこぴぺ。後半が自分なりになおしてみたやつ。

    static class LINQExtension
    {
        public static double Median(this IEnumerable<double> source)
        {
            if (source.Count() == 0)
            {
                throw new InvalidOperationException("Cannot compute median for an empty set.");
            }

            var sortedList = from number in source
                             orderby number
                             select number;

            int itemIndex = (int)sortedList.Count() / 2;

            if (sortedList.Count() % 2 == 0)
            {
                // Even number of items.
                return (sortedList.ElementAt(itemIndex) + sortedList.ElementAt(itemIndex - 1)) / 2;
            }
            else
            {
                // Odd number of items.
                return sortedList.ElementAt(itemIndex);
            }
        }

        public static double MedianEx(this IEnumerable<double> source)
        {
            var items = source.OrderBy(d => d).ToArray();
            if (!items.Any())
            {
                throw new InvalidOperationException("Cannot compute median for an empty set.");
            }

            int itemIndex = items.Length / 2;
            if (items.Length % 2 == 0)
            {
                return (items[itemIndex] + items[itemIndex - 1]) / 2;
            }
            else
            {
                return items[itemIndex];
            }
        }
    }

Ix-MainをNuGetで参照にくわえて10000個くらいのdoubleの配列に対して1000回くらいループ回してみて時間はかってみました。

var source = EnumerableEx.Generate(0.0, d => d <= 10000.0, d => ++d, d => d);
{
    var stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        source.Median();
    }
    stopwatch.Stop();
    Console.WriteLine("Median: {0}ms", stopwatch.ElapsedMilliseconds);
}
{
    var stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < 1000; i++)
    {
        source.MedianEx();
    }
    stopwatch.Stop();
    Console.WriteLine("MedianEx: {0}ms", stopwatch.ElapsedMilliseconds);
}
Median: 7290ms
MedianEx: 2538ms

ふむ2倍以上遅い。

ということで、何故遅いのかは夜遅いので誰かにパス・・・!