かずきのBlog@hatena

日本マイクロソフトに勤めています。このブログは個人ブログなので、ここに書いている内容は個人的な意見で会社の公式見解ではない点にご注意ください。好きなものは XAML と C# 。苦手なものは型の無い言語です。

方法: 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倍以上遅い。

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