2009-12-07 17 views
6

Excel VBAの[検索]と[バイナリ検索]の違いは何ですか?私のプラットフォームはOffice 11 | 2003で、列Aに対する文字列を3つの値で検索します。合計行数〜140,000Excel検索速度対VBAバイナリ検索?

価値がある場合は、ライブラリ&の関数を参照してソートしてからバイナリ検索する必要がありますか?文字列のバイナリ検索/テキストには潜在的に問題があります。

...あるもの に注意する必要があります。バイナリ検索 sortedtextrequires with formula 注意。 Aladin A., Excel MVP

Excelが検索:私の直感に対する多く

Worksheets(1).Range("A:A").Find("PN-String-K9", LookIn:=xlValues, LookAt:=xlWhole) 

答えて

7

をVBAバイナリ検索が強くExcelが検索よりも優れています。少なくとも以下のシナリオでは、120,000の6文字列が3つのワークシートにわたって均等に分散されています。

Excelの検索に1分58秒、
VBAバイナリ検索は、特定のマシンで36秒かかります。

テキストが順調であることを知っていることの利点は、Excelの自然な利点をはるかに上回っています。 Aladin Aはソート順について警告します。

Option Explicit 

' Call Search to look for a thousand random strings 
' in 3 worksheets of a workbook 

' requires a workbook with 3 sheets and 
' column A populated with values between "00001" to "120000" 
' split evenly 40,000 to a worksheet in ascending order. 
' They must be text, not numbers. 

Private Const NUM_ROWS As Long = 120000 
Private Const SHEET_1 As String = "Sheet1" 
Private Const SHEET_2 As String = "Sheet2" 
Private Const SHEET_3 As String = "Sheet3" 

' This uses VBA Binary Search 
Public Sub Search() 
    Worksheets(SHEET_1).Range("B:B").ClearContents 
    Worksheets(SHEET_2).Range("B:B").ClearContents 
    Worksheets(SHEET_3).Range("B:B").ClearContents 
    DoSearch True  ' change to False to test Excel search 
End Sub 

' Searches for a thousand values using binary or excel search depending on 
' value of bBinarySearch 
Public Sub DoSearch(ByVal bBinarySearch As Boolean) 
    Debug.Print Now 
    Dim ii As Long 

    For ii = 1 To 1000 
     Dim rr As Long 
     rr = Int((NUM_ROWS) * Rnd + 1) 
     If bBinarySearch Then 
      Dim strSheetName As String 
      Dim nRow As Long 
      If BinarySearch(MakeSearchArg(rr), strSheetName, nRow) Then 
       Worksheets(strSheetName).Activate 
       Cells(nRow, 1).Activate 
      End If 
     Else 
      If Not ExcelSearch(SHEET_1, MakeSearchArg(rr)) Then 
       If Not ExcelSearch(SHEET_2, MakeSearchArg(rr)) Then 
        ExcelSearch SHEET_3, MakeSearchArg(rr) 
       End If 
      End If 
     End If 
     ActiveCell.Offset(0, 1).Value = "FOUND" 
    Next 
    Debug.Print Now 

End Sub 

' look for one cell value using Excel Find 
Private Function ExcelSearch(ByVal strWorksheet As String _ 
    , ByVal strSearchArg As String) As Boolean 
    On Error GoTo Err_Exit 
    Worksheets(strWorksheet).Activate 
    Worksheets(strWorksheet).Range("A:A").Find(What:=strSearchArg, LookIn:=xlValues, LookAt:= 
     xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True 
     , SearchFormat:=False).Activate 
    ExcelSearch = True 
    Exit Function 
Err_Exit: 
    ExcelSearch = False 
End Function 

' Look for value using a vba based binary search 
' returns true if the search argument is found in the workbook 
' strSheetName contains the name of the worksheet on exit and nRow gives the row 
Private Function BinarySearch(ByVal strSearchArg As String _ 
    , ByRef strSheetName As String, ByRef nRow As Long) As Boolean 
    Dim nFirst As Long, nLast As Long 
    nFirst = 1 
    nLast = NUM_ROWS 
    Do While True 
     Dim nMiddle As Long 
     Dim strValue As String 
     If nFirst > nLast Then 
      Exit Do  ' Failed to find search arg 
     End If 
     nMiddle = Round((nLast - nFirst)/2 + nFirst) 
     SheetNameAndRowFromIdx nMiddle, strSheetName, nRow 
     strValue = Worksheets(strSheetName).Cells(nRow, 1) 
     If strSearchArg < strValue Then 
      nLast = nMiddle - 1 
     ElseIf strSearchArg > strValue Then 
      nFirst = nMiddle + 1 
     Else 
      BinarySearch = True 
      Exit Do 
     End If 
    Loop 
End Function 

' convert 1 -> "000001", 120000 -> "120000", etc 
Private Function MakeSearchArg(ByVal nArg As Long) As String 
    MakeSearchArg = Right(CStr(nArg + 1000000), 6) 
End Function 

' converts some number to a worksheet name and a row number 
' This is depenent on the worksheets being named sheet1, sheet2, sheet3 

' and containing an equal number of vlaues in each sheet where 
' the total number of values is NUM_ROWS 
Private Sub SheetNameAndRowFromIdx(ByVal nIdx As Long _ 
    , ByRef strSheetName As String, ByRef nRow As Long) 
    If nIdx <= NUM_ROWS/3 Then 

     strSheetName = SHEET_1 
     nRow = nIdx 
    ElseIf nIdx > (NUM_ROWS/3) * 2 Then 
     strSheetName = SHEET_3 
     nRow = nIdx - (NUM_ROWS/3) * 2 
    Else 
     strSheetName = SHEET_2 
     nRow = nIdx - (NUM_ROWS/3) 
    End If 
End Sub 
+0

ありがとうございます。 52000の可能性(単一シート)の中で1000の例を検索するテストケースを実行すると、バイナリサーチではExcel検索で17秒、5.5秒で得られます。擦れは25%の時間で二分探索が失敗したことです。私は問題がVBAの ">"と "<"の比較とは違って、文字列の並べ替えが異なると考えています。 – ExcelCyclist

+0

シェルでレコードのソートが行われ、バイナリ検索がうまくいきます! 2000年の無作為の例は、36秒で52000行(Excel検索)と11秒(バイナリ検索)で見つかった。 – ExcelCyclist

3

オートフィルタを使用すると、どの方法でも手動でレコードを検索するよりもはるかに高速です。

フィルターをかけて結果があるかどうかを確認してから、次に進みます。いずれかが見つかった場合(結果の数をチェックすることによって)、手動でフィルタリングされた小さな部分を検索したり、すべてを返すことができます。

私はそれを約44,000レコードで使用し、それに対して100+のリストを探しました。

注意しないと、バイナリ検索が無限ループで簡単に停止することがあります。

3

ソート済みオプションでvlookupを使用すると、vbaよりも高速になる可能性があります。