簡體   English   中英

用於查看算法花費多長時間的計時器表示我的二進制搜索比線性搜索花費的時間更長

[英]A timer for seeing how long an algorithm is taking is saying my binary search takes longer than a linear search

這是關於要點的課程https://gist.github.com/2605302

我已經使用不同的文件對它進行了多次測試,即使對二進制搜索進行的比較較少,所以花費的時間總是更多。 出了什么問題?

public static int linerSearch ( String array [], String word, long resultsArray [])
{
    int comparisons = 0;
    int pos = -1;
    //i have started the timer where the search actualy starts
    long start = System.nanoTime ();
    for (int i = 0; i < array.length; i++){
        comparisons = comparisons + 1;
        if (array [i].equals (word)){
            pos = i;
            break;
        }
    }
    long stop = System.nanoTime ();
    long total = stop - start;
    resultsArray [0] = total;
    resultsArray [1] = (long) (long) array.length;
    resultsArray [2]= (long) (long) comparisons;
    return pos;
}

這是下一個binarySearch類

public  static int binarySearch (String [] array, String word, resultsArray []) {
    int start = 0;
    int end = array.length - 1;;
    int midPt;
    int pos = -1;
    int comparisons2 = 0;
    long start2 = System.nanoTime ();
    Arrays.sort (array);
    while (start <= end) {
        midPt = (start + end) / 2;
        comparisons2 = comparisons2 + 1;
        if (array [midPt].equalsIgnoreCase (word)) {
            pos = midPt;
            break;
        }
        else if (array [midPt].compareToIgnoreCase (word) < 0) {
            start = midPt + 1;
            comparisons2 = comparisons2 + 1;
            //camparisons2 addition was added inside this elseif and other elseif as a work around for not breaking the elseif statement tree, if it has made it two the last elseif then two camparisons after the first one will have been done
        } else if (array [midPt].compareToIgnoreCase (word) > 0)  {
            comparisons2 = comparisons2 + 2;
            end = midPt - 1;
        }
    }
    long stop2 = System.nanoTime ();
    long total2 = stop2 - start2;
    resultsArray [0] = total2;
    resultsArray [1] = (long) (long) array.length;
    resultsArray [2]= (long) (long) comparisons2;
    return pos;
}

編輯:我還應該補充一點,我在一個已經排序過的數組上嘗試了它,沒有那行代碼,它仍然是一個更長的時間,它不應該

您的基准測試的問題是Arrays.sort(數組)花費大部分時間並且不計算它的比較。 線性搜索需要N次比較。 排序數組時,您需要花費超過N次比較。

要查看二進制搜索速度更快,您應該進行此類測試:

1)使用線性搜索搜索1000次不同的元素

2)對數組進行一次排序,並使用二進制搜索1000次搜索不同的元素

您的基准存在缺陷,原因有很多:

  • 我們不知道文件的內容。 如果搜索到的單詞位於開頭,則線性搜索將比二進制搜索更快
  • 線性搜索與equals進行比較,而二進制搜索與equalsIgnoreCase進行比較
  • 你沒有足夠多次執行代碼讓JIT編譯代碼

我還沒有驗證你的二進制搜索算法是否正確,但為什么不使用與JDK捆綁的那個(在java.util.Arrays類中)。

無論如何,你不需要測量任何東西。 平均而言,二進制搜索比線性搜索更快。 無需再證明這一點。

好的,我已經為你一勞永逸地解決了這個問題。 首先,這是我使用的二進制搜索方法:

public static int binarySearch(String[] array, String word, long resultsArray[]) {
    int start = 0;
    int end = array.length - 1;
    int midPt;
    int pos = -1;
    int comparisons2 = 0;

    //Arrays.sort(array);

    long start2 = System.nanoTime();
    while (start <= end) {
        midPt = (start + end) / 2;
        int comparisonResult = array[midPt].compareToIgnoreCase(word);
        comparisons2++;
        if (comparisonResult == 0) {
            pos = midPt;
            break;
        } else if (comparisonResult < 0) {
            start = midPt + 1;
        } else { // comparisonResult > 0
            end = midPt - 1;
        }
    }
    long stop2 = System.nanoTime();
    long total2 = stop2 - start2;

    resultsArray[0] = total2;
    resultsArray[1] = (long) array.length;
    resultsArray[2] = (long) comparisons2;
    return pos;
}

您會注意到我通過保存比較結果並使用它來減少比較次數。

接下來,我下載了這個235882字的列表 它已經被排序而忽略了這種情況。 然后,我構建了一個測試方法,將該文件的內容加載到一個數組中,然后使用這兩種搜索方法查找該列表的每個單詞。 然后,它分別平均每種方法的比較次數和次數。

我發現你必須小心選擇使用哪種比較方法: 如果你使用Arrays.sort(...)列表並在二進制搜索中使用compareToIgnoreCase ,它就會失敗! 失敗我的意思是它找不到給定列表中的單詞,即使該單詞實際存在於那里。 這是因為Arrays.sort(...)是一個區分大小寫的字符串排序器。 如果使用它,則必須使用compareTo(...)方法。

所以,我們有兩個案例

  1. 不區分大小寫的列表和compareToIgnoreCase的使用
  2. 區分大小寫的列表和compareTo的使用

除了二進制搜索中的這些選項之外,您還可以在線性搜索中使用選項:是使用equals還是使用equalsIgnoreCase 我對所有這些案例進行了測試並對它們進行了比較。 平均結果:

  • equals線性搜索:時間:725536 ns; 比較:117941; 時間/比較:6.15 ns
  • 使用equalsIgnoreCase線性搜索:時間:1064334 ns; 比較:117940; 時間/比較:9.02 ns
  • 使用compareToIgnoreCase二進制搜索:時間:1619 ns; 比較:16; 時間/比較:101.19 ns
  • 使用compareTo二進制搜索:時間:763 ns; 比較:16; 時間/比較:47.69 ns

所以,現在我們可以清楚地看到你的問題: compareToIgnoreCase方法花費的時間是equals方法的16倍! 因為平均而言,需要二元搜索16比較才能找到給定的單詞,因此您可以在此時執行124次線性比較。 因此,如果您使用比這更短的單詞列表進行測試,則線性搜索確實總是比二進制搜索更快,因為它們使用的方法不同。

實際上,我還計算了線性搜索能夠比二進制搜索更快找到的單詞數:164使用compareTo方法時,使用compareToIgnoreCase方法時為614。 在235882個單詞的列表中,這個數字約為0.3%。 總而言之,我認為二進制搜索比線性搜索更快仍然是安全的。 :)

在你問之前的最后一點:我從binarySearch方法中刪除了排序代碼,因為這實際上是完全不同的東西。 由於您正在比較兩種搜索算法,如果您將排序算法的成本添加到其數字中,則對另一種搜索算法不公平。 我已經在另一個答案中發布了以下評論作為評論,但為了完整起見,我將在此處復制:

二進制搜索會增加排序的開銷成本。 因此,如果您只需要從數組中找到一個元素,則線性搜索總是更快,因為排序至少需要O(n log n)時間,然后二進制搜索需要O(log n)時間,由O(n)控制記錄n)操作。 線性搜索在O(n)時間內執行,該時間優於O(n log n)。 但是一旦你對數組進行了排序,O(log n)就好於O(n)。

如果你堅持在binarySearch方法中使用排序命令,你應該知道,通過我的設置排序,初始隨機順序中的長字列表平均需要超過140000000 ns或0.14秒。 在這段時間里,你可以執行使用一些23000000比較equals的方法,所以你如果)你的陣列是一個隨機的順序, 真的 應該使用二進制搜索b)如果你只輩子必須找個只是一個或幾個元素那里。

還有一件事。 在此示例中,您在String數組中搜索單詞時,訪問數組中項目的成本可以忽略不計,因為該數組保存在計算機的快速主內存中。 但是,如果你有,說,下令文件的一個巨大的一堆和你試圖找到他們的東西,然后訪問一個文件將盡一切其他計算的成本可以忽略不計,而不是成本。 所以二元搜索在這種情況下也會完全搖擺不定。

您的代碼不會測量二進制搜索,也會在搜索之前對數組進行排序。 這將始終比簡單的線性搜索更長。

} else if (array [midPt].compareToIgnoreCase (word) > 0)  {

你根本不需要這個測試。 在代碼的這一點上,沒有其他可能性。 它並不平等,它不低於:你已經測試過了; 所以它必須大於。

因此,您可以將比較減少33%。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM