6

さまざまな数値型 (double、float など) を含む配列を並べ替えたいと考えています。このコードは System.ArgumentException ("Value is not a System.Single") エラーを発生させます。

new dynamic[] { 5L, 4D, 3F, 2U, 1M, 0UL }.ToList().Sort();

私はこれを行うためにLINQを使用できることを知っています:

new dynamic[] { 5L, 4D, 3F, 2U, 1M, 0UL }.ToList().OrderBy(x => (decimal)x).ToArray();

しかし、各要素をキャストする必要がない、より高速な方法はありますか?

補遺: リスト内の null も処理できるようにしたいので、LINQ クエリのキャストでさえ役に立ちません。

4

1 に答える 1

5

まず、ToList() を削除します。この操作は必要ありませんので、削除してください。それは物事を少しスピードアップします。

残りの部分: いいえ、これより速い方法はありません。最初に 2 倍のパフォーマンス向上を示すコードで回答を投稿した場合。

なんで?OrderBy 全体は、キーの生成と、それらのキーを比較することによる値のソートの 2 つの部分に分かれています。配列内のアイテムの数が増えると、キーの生成は自然に増えますが、比較操作の数は指数関数的に増えます。

私のコードでは、すべての値を 10 進数に変換する必要はありませんでしたが (キー生成中の速度が向上しました)、ソート中に値をボックス化解除する必要がありました。これはパフォーマンスの低下です。より大きな配列では、比較操作の数が指数関数的に増加したため、ボックス化解除の数が増加し、最終的には砂の中の砂でした.

2種類の数値を受け入れるデリゲートを作成し、そのデリゲートで、両方のタイプの最適な比較ソリューションを備えた動的なコンパイル済みコードを作成するなど、他のソリューションを試しました。これには、数値を変換する必要がある場合が常にあります。そして、仕分けフェイズの間、これは殺しになります。

簡単に言えば、いいえ、ルーチンをより速くすることはできません. コンペアフェイズが可能な限り高速である限り、鍵生成フェイズにかかる時間は問題ではありません。

興味のある人は、以前の回答の元のコードを参照してください(配列が大きいほど高速ではありません)。

    static private dynamic[] testSet = new dynamic[] { 5L, 4D, 3F, null, 2U, 1M, null, 0UL };

    static void Main(string[] args)
    {
        Stopwatch st1 = new Stopwatch();
        st1.Start();
        for(int i = 0; i < 100000; i++)
            Test1();
        st1.Stop();

        Stopwatch st2 = new Stopwatch();
        st2.Start();
        for(int i = 0; i < 100000; i++)
            Test2();
        st2.Stop();
    }

    static public void Test1()
    {
        var result = testSet.OrderBy(x => x == null ? (decimal?)null : (decimal)x).ToArray();
    }

    static public void Test2()
    {
        var result = testSet.OrderBy(x => (object)x, new HeterogeneousNumbersComparer()).ToArray();
    }

    public class HeterogeneousNumbersComparer : IComparer<object>
    {
        public int Compare(object a, object b)
        {
            if (a == null)
                if (b == null)
                    return 0;
                else
                    return -1;
            else if (b == null)
                return +1;

            if (a.GetType() == b.GetType())
            {
                switch(Type.GetTypeCode(a.GetType()))
                {
                    case TypeCode.Byte:
                        return ((Byte)a).CompareTo((Byte)b);
                    case TypeCode.Decimal:
                        return ((Decimal)a).CompareTo((Decimal)b);
                    case TypeCode.Double:
                        return ((Double)a).CompareTo((Double)b);
                    case TypeCode.Int16:
                        return ((Int16)a).CompareTo((Int16)b);
                    case TypeCode.Int32:
                        return ((Int32)a).CompareTo((Int32)b);
                    case TypeCode.Int64:
                        return ((Int64)a).CompareTo((Int64)b);
                    case TypeCode.SByte:
                        return ((SByte)a).CompareTo((SByte)b);
                    case TypeCode.Single:
                        return ((Single)a).CompareTo((Single)b);
                    case TypeCode.UInt16:
                        return ((UInt16)a).CompareTo((UInt16)b);
                    case TypeCode.UInt32:
                        return ((UInt32)a).CompareTo((UInt32)b);
                    case TypeCode.UInt64:
                        return ((UInt64)a).CompareTo((UInt64)b);
                }
            }
            return Convert.ToDecimal(a).CompareTo(Convert.ToDecimal(b));
        }
    }
}

数値 (私のマシンでは):
Test1: 550ms
Test2: 263ms
だから... 係数 2 !!!

于 2013-04-27T13:30:46.177 に答える