繁体   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