2009-06-08 8 views
35

簡単な質問 - 与えられたIList<T>バイナリ検索は、バイナリ検索をサポートしているタイプにデータをコピーせずに、自分でメソッドを記述することなく、どのように実行しますか?私の現在の状態は以下の通りです。IList <T>でバイナリ検索を実行するにはどうすればよいですか?

  • List<T>.BinarySearch()私は傾向があるArrayList.Adapter()を使用して

ことはできませんため、IListから継承されませんList<T>

  • IList<T>ためArrayList.Adapter()方法の同等はありませんIList<T>
  • のメンバーではありませんビルドインの方法では不可能だと信じていますが、そのような基本的な方法がBCL/FCLにはないとは思えません。

    IList<T>の最短、最速、賢明、または最も美しいバイナリ検索の実装を提供できるのは誰ですか?

    UPDATE

    我々は、すべてのリストは、あなたがそれであると仮定することができ、したがって、バイナリ検索を使用する前にソートされなければならないことを知っています。しかし、私はそれがソートと同じ問題だと仮定しますが(検証しませんでした)、どのように並べ替えますかIList<T>

    結論

    なしビルドでIList<T>のためのバイナリ検索はないように思えます。 First()OrderBy() LINQメソッドを使用して検索と並べ替えを行うことができますが、同様にパフォーマンスが低下します。あなた自身で(拡張メソッドとして)それを実装することがあなたができる最高のようです。あなたが言及したようにあなたは、バイナリ検索IList<T>問題のカップルを持ってしようとしている

  • +2

    古いデータに対してバイナリ検索を実行することはできません。適切にソートされていて、最初に重複がなくてはなりません。 –

    +1

    リストがソートされていると仮定できます。 –

    +0

    オブジェクトの基本的なタイプを知っていますか?リストは、SortメソッドとBinarySearchメソッドを提供しません。 –

    答えて

    29

    .NETのような一般的な目的のバイナリ検索方法はありますが、いくつかの基本クラスには存在しますが(インタフェースにはないようですが)、私の汎用目的のものがあります。

    public static Int32 BinarySearchIndexOf<T>(this IList<T> list, T value, IComparer<T> comparer = null) 
    { 
        if (list == null) 
         throw new ArgumentNullException(nameof(list)); 
    
        comparer = comparer ?? Comparer<T>.Default; 
    
        Int32 lower = 0; 
        Int32 upper = list.Count - 1; 
    
        while (lower <= upper) 
        { 
         Int32 middle = lower + (upper - lower)/2; 
         Int32 comparisonResult = comparer.Compare(value, list[middle]); 
         if (comparisonResult == 0) 
          return middle; 
         else if (comparisonResult < 0) 
          upper = middle - 1; 
         else 
          lower = middle + 1; 
        } 
    
        return ~lower; 
    } 
    

    これはもちろん、比較対象者が使用するのと同じ規則に従って、問題のリストが既にソートされていることを前提としています。

    +0

    IList にはCountメソッドがありますか?私はドキュメントには表示されません。 –

    +1

    IList にはCountプロパティがあります。http://msdn.microsoft.com/en-us/library/s16t9z9d.aspx –

    +0

    プロパティは、ページの下部にあります。 –

    4

    、まず、List<T>BinarySearch方法はIList<T>インターフェースのメンバーではありません。次に、検索する前にリストをソートする方法がありません(バイナリ検索が機能するために行う必要があります)。

    新しいList<T>を作成し、並べ替えてから検索することをお勧めします。完璧ではありませんが、IList<T>があれば多くのオプションを用意する必要はありません。

    +0

    リストがソートされていると仮定できます。しかし、私は、実際にソートと同じ問題があると仮定します - IListを並べ替える方法? –

    +0

    コピーはO(n)なので、絶対にオプションはありません。バイナリ検索はあまり意味がありません...;) –

    +0

    なぜソートの方法はありませんか? IList はランダムアクセスを許可しているので、T型のオブジェクトを比較する能力があれば、あらかじめソートすることができます。 –

    -2

    ListとIListにはBinarySearchメソッドはありませんが、SortedListはそのようにしています。

    +4

    リストには、BinarySearch()メソッドがあります。 –

    +2

    とsortedlistはありません。 – nawfal

    -1

    .NET 3を使用できる場合は、5、あなたはLINQの拡張メソッドでビルドを使用することができます。

    using System.Linq; 
    
    IList<string> ls = ...; 
    var orderedList = ls.OrderBy(x => x).ToList(); 
    orderedList.BinarySearch(...); 
    

    しかし、これは本当にアンドリューハレのソリューションについて行くのわずかに異なる方法であり、あなたがのオフを複数回検索している場合のみ、本当に便利です同じ順序リスト。

    +5

    ToList()はすべての項目を新しいリストにコピーします。次に、リスト .BinarySearch()を使用します。コピーはO(n)なので、バイナリ検索と併用することはできません。 –

    +1

    これが単発呼び出しであれば、それは当てはまります。ただし、順序付きリストを格納し、複数のバイナリ検索をオフにすると、O(n)は1回限りのヒットとなります。これは、あなたがhttp://stackoverflow.com/a/967081/24954で得られるのとまったく同じパフォーマンスです。 – Nathan

    29

    私は拡張メソッドのソリューションが好きです。しかし、少しの警告が必要です。

    これは彼の著書Programming Pearlsの事実上Jon Bentleyの実装であり、数字のオーバーフローのバグは妥当ではなく、20年ほどの間発見されていません。 IListに多数のアイテムがある場合、(上部+下部)がInt32をオーバーフローさせる可能性があります。これを解決するには、代わりに減算を使用して中間計算を少しずつ行う必要があります。中間=下+(上 - 下)/ 2;

    ベントレーはまた、二分探索アルゴリズムを1946年に出版された、あなたはのための既製の実装が必要な場合は最初の正しい実装は1962年

    http://en.wikipedia.org/wiki/Binary_search#Numerical_difficulties

    +5

    これは、ビルドインの実装を使用したかった理由です。バイナリ検索を行うのは非常に難しいです。 +1 –

    +1

    それは大きなポイントです!個人的には、バイナリ検索を使って2^30以上のアイテムを調べることはできません。もちろん、プログラマーのコレクションが本当に大規模で完全にRAMにロードされている場合、彼は自分のコンピューターに何を求めているのか、すでに注意を払っていないと感じています。 – scraimer

    +0

    本当にスクレイマー?あなたはRAMの4GB以上を使用するとばかげていると思いますか?ラムの4TBはばかげていると私たちに言うために30年後に利用できるようになりますか? – rocketsarefast

    2

    まで公表されなかったことをプログラミング真珠に警告しIList<T> S、Wintellect's Power Collectionshas oneAlgorithms.cs中)のバイナリ検索:

    /// <summary> 
    /// Searches a sorted list for an item via binary search. The list must be sorted 
    /// by the natural ordering of the type (it's implementation of IComparable&lt;T&gt;). 
    /// </summary> 
    /// <param name="list">The sorted list to search.</param> 
    /// <param name="item">The item to search for.</param> 
    /// <param name="index">Returns the first index at which the item can be found. If the return 
    /// value is zero, indicating that <paramref name="item"/> was not present in the list, then this 
    /// returns the index at which <paramref name="item"/> could be inserted to maintain the sorted 
    /// order of the list.</param> 
    /// <returns>The number of items equal to <paramref name="item"/> that appear in the list.</returns> 
    public static int BinarySearch<T>(IList<T> list, T item, out int index) 
         where T: IComparable<T> 
    { 
        // ... 
    } 
    
    -1

    は、バイナリ検索を終了することができることに注意してくださいリストの実装によっては非効率的です。たとえば、リンクされたリストの場合は、正しく実装するとO(n)、それ以外の場合はO(n log n)です。

    +0

    一般的なリストの文脈で役に立つステートメントではあるが、質問はIList 'についてのもので、索引によるO(1)アクセスを提供すると考えられているので、あなたは下降投票されたと仮定する。少なくとも.Net自身の 'LinkedList 'は 'IList 'を実装していません - それはそれがまさにその理由です。もちろん、誰でもO(n)バージョンのIList .Item [index] 'を実装することができます。 –

    27

    ここに私のLasseのコードのバージョンがあります。ラムダ式を使用して検索を実行できると便利です。オブジェクトのリストを検索する場合、ソートに使用されたキーだけを渡すことができます。 IComparerを使用する実装は、自明にこの実装から導出されます。

    また、一致するものが見つからなかった場合は〜lowerに戻りたいと思います。 Array.BinarySearchはそれを行い、注文を維持するために検索する項目を挿入する必要がある場所を知ることができます。

    以下アントワーヌによって提供された実装にバグがあること
    /// <summary> 
    /// Performs a binary search on the specified collection. 
    /// </summary> 
    /// <typeparam name="TItem">The type of the item.</typeparam> 
    /// <typeparam name="TSearch">The type of the searched item.</typeparam> 
    /// <param name="list">The list to be searched.</param> 
    /// <param name="value">The value to search for.</param> 
    /// <param name="comparer">The comparer that is used to compare the value with the list items.</param> 
    /// <returns></returns> 
    public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer) 
    { 
        if (list == null) 
        { 
         throw new ArgumentNullException("list"); 
        } 
        if (comparer == null) 
        { 
         throw new ArgumentNullException("comparer"); 
        } 
    
        int lower = 0; 
        int upper = list.Count - 1; 
    
        while (lower <= upper) 
        { 
         int middle = lower + (upper - lower)/2; 
         int comparisonResult = comparer(value, list[middle]); 
         if (comparisonResult < 0) 
         { 
          upper = middle - 1; 
         } 
         else if (comparisonResult > 0) 
         { 
          lower = middle + 1; 
         } 
         else 
         { 
          return middle; 
         } 
        } 
    
        return ~lower; 
    } 
    
    /// <summary> 
    /// Performs a binary search on the specified collection. 
    /// </summary> 
    /// <typeparam name="TItem">The type of the item.</typeparam> 
    /// <param name="list">The list to be searched.</param> 
    /// <param name="value">The value to search for.</param> 
    /// <returns></returns> 
    public static int BinarySearch<TItem>(this IList<TItem> list, TItem value) 
    { 
        return BinarySearch(list, value, Comparer<TItem>.Default); 
    } 
    
    /// <summary> 
    /// Performs a binary search on the specified collection. 
    /// </summary> 
    /// <typeparam name="TItem">The type of the item.</typeparam> 
    /// <param name="list">The list to be searched.</param> 
    /// <param name="value">The value to search for.</param> 
    /// <param name="comparer">The comparer that is used to compare the value with the list items.</param> 
    /// <returns></returns> 
    public static int BinarySearch<TItem>(this IList<TItem> list, TItem value, IComparer<TItem> comparer) 
    { 
        return list.BinarySearch(value, comparer.Compare); 
    } 
    
    +0

    本当に、このコードをコピーする必要があるかどうかはわかりません(組み込みの.Netのように動作するため)。 –

    +1

    必要に応じて、[BinarySearchExtensions](http://www.google.com/codesearch#rb-wOGTl7tU/trunk/src/SixPack/Collections/Algorithms/BinarySearchExtensions.cs&q=BinarySearchExtensions%20package:http:/)を使用できます。/Sixpackライブラリー(http://nuget.org/List/Packages/SixPack)のクラス(/sixpack-library%5C.googlecode%5C.com)を参照してください。 –

    +1

    Naaw、私はBCLとの一貫性を保つためにあなたに拍手を送っています。正直なところ正解です。 –

    3

    注:リスト内の任意のより大きなアイテムを検索する場合。戻り値は、〜ではなく〜である必要があります。 DecompileメソッドArraySortHelper.InternalBinarySearch(mscorlib)を使用して、フレームワークの実装を確認します。

    +1

    この問題を発見していただきありがとうございます。バイナリ検索は間違いなく正しいです! 質問に返信する代わりに、回答にコメントする必要があります。これにより、関連する投稿を簡単に見つけることができます。 –

    3

    私はしばらくこの権利を得ることに苦労しています。特に、MSDNで指定されているようなエッジケースの戻り値:

    .NET 4.0からArraySortHelper.InternalBinarySearch()をコピーし、さまざまな理由で自分の味を作りました。

    用途:

    var numbers = new List<int>() { ... }; 
    var items = new List<FooInt>() { ... }; 
    
    int result1 = numbers.BinarySearchIndexOf(5); 
    int result2 = items.BinarySearchIndexOfBy(foo => foo.bar, 5); 
    

    これは、すべての.NET型で動作するはずです。私は今までint、long、doubleを試してきました。

    実装:

    public static class BinarySearchUtils 
    { 
        public static int BinarySearchIndexOf<TItem>(this IList<TItem> list, TItem targetValue, IComparer<TItem> comparer = null) 
        { 
         Func<TItem, TItem, int> compareFunc = comparer != null ? comparer.Compare : (Func<TItem, TItem, int>) Comparer<TItem>.Default.Compare; 
         int index = BinarySearchIndexOfBy(list, compareFunc, targetValue); 
         return index; 
        } 
    
        public static int BinarySearchIndexOfBy<TItem, TValue>(this IList<TItem> list, Func<TItem, TValue, int> comparer, TValue value) 
        { 
         if (list == null) 
          throw new ArgumentNullException("list"); 
    
         if (comparer == null) 
          throw new ArgumentNullException("comparer"); 
    
         if (list.Count == 0) 
          return -1; 
    
         // Implementation below copied largely from .NET4 ArraySortHelper.InternalBinarySearch() 
         int lo = 0; 
         int hi = list.Count - 1; 
         while (lo <= hi) 
         { 
          int i = lo + ((hi - lo) >> 1); 
          int order = comparer(list[i], value); 
    
          if (order == 0) 
           return i; 
          if (order < 0) 
          { 
           lo = i + 1; 
          } 
          else 
          { 
           hi = i - 1; 
          } 
         } 
    
         return ~lo; 
        } 
    } 
    

    ユニットテスト:それは箱から出して動作しない場合、私はちょうど私自身のコードからこれをシザリング

    [TestFixture] 
    public class BinarySearchUtilsTest 
    { 
        [Test] 
        public void BinarySearchReturnValueByMsdnSpecification() 
        { 
         var numbers = new List<int>() { 1, 3 }; 
    
         // Following the MSDN documentation for List<T>.BinarySearch: 
         // http://msdn.microsoft.com/en-us/library/w4e7fxsh.aspx 
    
         // The zero-based index of item in the sorted List(Of T), if item is found; 
         int index = numbers.BinarySearchIndexOf(1); 
         Assert.AreEqual(0, index); 
    
         index = numbers.BinarySearchIndexOf(3); 
         Assert.AreEqual(1, index); 
    
    
         // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item 
         index = numbers.BinarySearchIndexOf(0); 
         Assert.AreEqual(~0, index); 
    
         index = numbers.BinarySearchIndexOf(2); 
         Assert.AreEqual(~1, index); 
    
    
         // or, if there is no larger element, the bitwise complement of Count. 
         index = numbers.BinarySearchIndexOf(4); 
         Assert.AreEqual(~numbers.Count, index); 
        } 
    } 
    

    は、そうコメントしてください。

    これは、少なくともMSDNの仕様に従って、実用的な実装で問題を解決することを望んでいます。

    +0

    あなたは[アントワーヌの答え](http://stackoverflow.com/a/2948872/709537)を見逃したようでした。 2010年以来存在しており、あなたの答えは冗長になります。 –

    +1

    あなたは彼の答えに対する私のコメントを見逃しました。少なくとも、MSDNの仕様に従って、実装に欠陥があると考えています。私が間違っていれば私を修正してください。 – angularsen

    +0

    あなたは正しいです。 Antoineがあなたのコメントの後でそれを修正して以来、今は冗長です。私はちょうどあなたの答えをupvoted。ただし、あなたの見た目に重複している回答がその前文として存在する理由についてのコメントを追加することを検討することもできます(これは現在コーナーケースのみの一般的なコメントです。 –

    2

    List<T>.BinarySearch(T item)を使用できます。カスタム比較対象を使用する場合は、List<T>.BinarySearch(T item, IComparer<T> comparer)を使用してください。詳細については、このMSDN linkをご覧ください。

    関連する問題