かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

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

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