[英]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次搜索不同的元素
您的基准存在缺陷,原因有很多:
我还没有验证你的二进制搜索算法是否正确,但为什么不使用与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(...)
方法。
所以,我们有两个案例
compareToIgnoreCase
的使用 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.