2012-02-12 5 views
3

配列の各文字列要素内の数値に基づいて文字列の配列を並べ替える効率的な方法を見つけようとしています。これは非常にうまく動作しますが、時にはそれがあまりにもかかる文字列配列のカスタム並べ替えのパフォーマンスを向上

class StringComparer : IComparer<string> 
{ 
    public int Compare(string a, string b) 
    { 
     string s1 = a; 
     string s2 = b; 

     Match matchA = Regex.Match(s1, @"\d+$"); 
     Match matchB = Regex.Match(s2, @"\d+$"); 

     long numberA = long.Parse(matchA.Value); 
     long numberB = long.Parse(matchB.Value); 

     if (numberB - numberA < 0) 
     { 
      return -1; 
     } 
     else 
     { 
      return 1; 
     } 
    } 
} 

:私は現在、(降順でソート)私のカスタムの比較子クラスでのArray.sort(配列、customComparer)静的メソッド(クイックソート)は、ビーイングを使用しています2.4Ghzプロセッサで1分以上かかる100 000文字列の配列でソートするのに多くの時間がかかります。私は同じことを達成するためのより効率的な方法があるのだろうかと思います。たとえば、別のソートアルゴリズムを実装するか、辞書を使用して値をソートするなどの別のアプローチをとることができます(値は文字列の数値部分です)。助言がありますか?前もって感謝します!

+1

"辞書を使用して値をソートする"非常に有望ですね。あなたはそれを試しましたか? –

+0

この種のソートを対象とする[基数ソート](http://en.wikipedia.org/wiki/Radix_sort)を試してみることができます:「同じ重要な位置を共有する個々の数字でキーをグループ化することによって整数キーを使ってデータをソートする値 " –

+1

@ Luigi Mendoza:ソートアルゴリズムはボトルネックではありません。 – jason

答えて

2

まず、同じ文字列を何度も解析する必要があります(正規表現とマッチングして一致を解析する)。代わりに、あなたが持っているものをカスタムタイプにカプセル化して、一度だけ解析するだけです。

public class FooString { 
    private readonly string foo; 
    private readonly long bar; 

    public FooString(string foo) { 
     this.foo = foo; 
     Match match = Regex.Match(foo, @"\d+$"); 
     this.bar = Int64.Parse(match.Value); 
    } 

    public string Foo { get { return this.foo; } } 
    public long Bar { get { return this.bar; } } 
} 

私もfooは、正規表現を満たさなければならないことを言って、このクラスにContract.Requiresを追加すると思います。

第二に、あなたは(あなたの場合には、正規表現と一致しないとlongに解析できないstring秒)Tの特定の値に死ぬIComparer<T>を持っています。これは一般的には悪い考えです。

ので、FooStringのための比較演算を行います。

public FooStringComparer : IComparer<FooString> { 
    public int Compare(FooString a, FooString b) { 
     Contract.Requires(a != null); 
     Contract.Requires(b != null); 
     return a.Bar.CompareTo(b.Bar); 
    } 
} 

は今、あなたのソートが驚くほど速くなりますが、何度も同じ文字列を解析停止したので。

+0

あなたのアプローチは素晴らしいです!私は100 000要素でテストしました。ソートのプロセスはほんの数秒しかかかりませんでした。これまで1分以上かかっていました!時間がかかる唯一の部分は、 'CustomString [] arrayOfCustomStrings = new CustomString [matchCollection.Count];で行うCustomStringの配列を開始することです。配列の作成には約12秒かかりますが、その後は2〜2秒でソートされます。レコードのためだけに、this.bar = Int64.Parse(match.value);に 'this.bar = Int64.Parse(match)'を変更する必要がありました。これは非常に役に立ちました。ありがとうジェイソン! – cake

5

各比較の値を解析しています。 を一度のように解析して、文字列/ロングペアを取得してソートし、その後に文字列部分を抽出することをお勧めします。

既存のコードにはバグがあります。はありませんは2つの文字列が等しいと比較して0を返します。

ここでLINQ使用して別のアプローチです(ソートでは、場所ではなく、シンプルである。)

var sorted = unsorted.OrderBy(x => long.Parse(Regex.Match(x, @"\d+$").Value)); 
        .ToList(); 

OrderByプロジェクトに一度のキーを取得するには、その後、キーを比較します。)

+0

LINQはいいですね。テストします。しかし、私は.NET 2.0をターゲットにしています。ありがとう! – cake

+0

@cake:LINQBridgeまたは.NET 2.0のオブジェクトにLINQを使用するのと同様のものを使用できます。 (将来的にはフレームワークの非常に古いバージョンが必要な場合は、そのように答えてください)基本的に同じことをやりなおすことができます:すべてのキーを一度取り除いてからソートするそれらと。 –

+0

申し訳ありませんが必要なバージョンに言及することを忘れている。ジェイソンが提案したように本当に速く比較するカスタムの文字列クラスを選択しました。私はLinqBridgeを認識していませんでしたが、間違いなくそれを調べて比較します。ありがとう! – cake

3

Regexes O(n log n)回実行しています。

は、O(n)のレッグ式の実行を必要とする数値を抽出し、SortedDictionary<long, string>

これに追加し、すべての文字列の上に一度ループを検討してください。残りの並べ替えは同等でなければなりません。

1

Compiledオプションを使用してRegexを1回だけ作成します。これにより速度が向上します。

class StringComparer : IComparer<string> 
{ 
    private static Regex _regex = new Regex(@"\d+$", RegexOptions.Compiled); 

    public int Compare(string a, string b) 
    { 
     long numberA = Int64.Parse(_regex.Match(a).Value); 
     long numberB = Int64.Parse(_regex.Match(b).Value); 
     return numberA.CompareTo(numberB); 
    } 
} 
+0

デフォルトでは、正規表現はコンパイルされキャッシュされます。これで差はないはずです。 –

+0

これはボトルネックではありません。 – jason

+0

@HenkHolterman:OPは 'Regex.Match(s1、@" \ d + $ ");を使用してコンパイルされません。しかし、パターンがコンストラクタで渡されるとき、正規表現がデフォルトでコンパイルされることを指摘することで正しいでしょう。 –

関連する問題