在学习算法的过程中,我编写了代码以比较两种算法在运行时间方面的性能。这些算法的任务是找到一个数组中所有相加的数字对,这些数字对加起来就是一个特定的数字。
第一种方法-蛮力。
2个for
循环查找与给定数字相加的数字对。时间复杂度基本上是O(n * n)。
第二种方法-高效 首先对数组进行排序,然后将开始和结束作为数组开始和结束的索引,然后根据位置中这些元素的总和向左或向右移动以找到数字对。
我的问题是- 我正在打印每种算法方法的运行时间。但是,蛮力法的运行时间似乎比高效方法快。为什么会这样呢?
在此处查看代码-
public class MainRunner {
final private static int numberRange = 100000;
public static void generateRandomNumbers(int[] array, int[] dupArray) {
System.out.println("Generated Array: ");
Random random = new Random();
for (int i = 0; i < array.length; i++) {
int generatedRandomInt = random.nextInt(array.length) + 1;
array[i] = dupArray[i] = generatedRandomInt;
}
}
public static void main(String[] args) {
int[] array = new int[numberRange];
int[] dupArray = new int[numberRange];
generateRandomNumbers(array, dupArray);
Random random = new Random();
int sumToFind = random.nextInt(numberRange) + 1;
System.out.println("\n\nSum to find: " + sumToFind);
// Starting Sort and Find Pairs
final long startTimeSortAndFindPairs = System.currentTimeMillis();
new SortAndFindPairs().sortAndFindPairsOfNumbers(sumToFind, array);
final long durationSortAndFind = System.currentTimeMillis() - startTimeSortAndFindPairs;
// Starting Find Pairs
final long startTimeFindPairs = System.currentTimeMillis();
new FindPairs().findPairs(sumToFind, dupArray);
final long durationFindPairs = System.currentTimeMillis() - startTimeFindPairs;
System.out.println("Sort and Find Pairs: " + durationSortAndFind);
System.out.println("Find Pairs: " + durationFindPairs);
}
}
SortAndFindPairs.java
public class SortAndFindPairs {
public void sortAndFindPairsOfNumbers(int argNumberToFind, int[] array) {
Arrays.sort(array);
System.out.println("\n\nResults of Sort and Find Pairs: \n");
int startIndex = 0;
int endIndex = array.length - 1;
while (startIndex < endIndex) {
int sum = array[startIndex] + array[endIndex];
if (argNumberToFind == sum) {
//System.out.println(array[startIndex] + ", " + array[endIndex]);
startIndex++;
endIndex--;
} else if (argNumberToFind > sum) {
startIndex++;
} else {
endIndex--;
}
}
}
和FindPairs.java
public class FindPairs {
public void findPairs(int argNumberToFind, int[] array) {
System.out.println("\nResults of Find Pairs: \n");
int randomInt1 = 0;
int randomInt2 = 0;
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j < array.length; j++) {
int sum = array[i] + array[j];
if (argNumberToFind == sum) {
//System.out.println(array[i] + ", " + array[j]);
//randomInt1++;
//randomInt2--;
}
}
}
}}
仅在FindPairs.java中添加两个变量randomInt1
和randomInt2
时,才会看到运行时间差异。否则,FindPairs.java的运行时间比SortAndFindPairs.java少得多。那么,为什么只添加两个变量操作会增加这么多时间呢?按照惯例,简单的操作应消耗可忽略的时间。我在这里错过了什么吗?
numberRange = 1000000
的结果
查找对的结果:
排序和查找对:641 查找对:57
答案 0 :(得分:1)
如LIuxed所述,排序操作需要一些时间。如果您花时间进行排序,那为什么不利用列表项已排序的事实呢?
如果列表元素已排序,则可以使用二进制搜索算法...从数组的中间开始,然后检查是向上1/2还是向下1/2。结果,使用排序数组来查找值可以获得更快的性能。这样的算法已经在Arrays.binarySearch方法中实现。
请参见https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#binarySearch(int[],%20int)
当您只排序一次但搜索多次时,您会注意到差异。
答案 1 :(得分:1)
我认为问题在于您的编译器优化对您有帮助。我尝试了代码的不同排列,并发现FindPairs中的double for循环几乎什么也不做。因此,编译器可能会剥离一些代码。
我得到了这个数字以及您的代码的确切副本:
排序和查找对:43 查找对:13
始终如一(我多次运行以进行双重检查)每次的排序和查找速度都很慢。
但是后来我改变了内循环,什么也不做:
for (int j = i + 1; j < array.length; j++) {
//int sum = array[i] + array[j];
//if (argNumberToFind == sum) {
//System.out.println(array[i] + ", " + array[j]);
//randomInt1++;
//randomInt2--;
//}
你猜怎么着?我得到了:
排序和查找对:20 查找对:11
尝试了几次,数字非常相似。通过删除两个循环,查找对的运行时间为1。因此,我的猜测是,编译器的优化步骤可能是假设内部循环内的代码没有任何作用,因此将其删除。排序和查找中的代码更加智能,因此可以保留。
现在,我尝试了另一件事,我注释掉randomInt1的增量,但是保留了总和,如果有注释,
for (int j = i + 1; j < array.length; j++) {
//int sum = array[i] + array[j];
//if (argNumberToFind == sum) {
//System.out.println(array[i] + ", " + array[j]);
randomInt1++;
//randomInt2--;
//}
然后我得到了:
排序和查找对:42 查找对:5
哇,突然变得更快了! (也许编译器通过使用循环边界将for替换为randomInt1的算术计算?)
我的最后一次尝试。您会注意到这不是一个公平的比较,排序和查找涉及很多逻辑,而查找则没有。当找到一对时,它实际上什么也不做。因此,要使它变成苹果,我们要确保查找对确实可以做某事,并确保排序和查找执行相同的额外量(例如在方程式的两边加相同的数字)。因此,我更改了计算匹配对数的方法。像这样:
System.out.println("\nResults of Find Pairs: \n");
long randomInt1 = 0;
int randomInt2 = 0;
int count = 0;
for (int i = 0; i < array.length - 1; i++) {
for (int j = i + 1; j < array.length; j++) {
int sum = array[i] + array[j];
if (argNumberToFind == sum) {
count++;
}
}
}
System.out.println("\nPairs found: " + count + "\n");
和
public void sortAndFindPairsOfNumbers(int argNumberToFind, int[] array) {
Arrays.sort(array);
System.out.println("\n\nResults of Sort and Find Pairs: \n");
int startIndex = 0;
int endIndex = array.length - 1;
int count = 0;
while (startIndex < endIndex) {
int sum = array[startIndex] + array[endIndex];
if (argNumberToFind == sum) {
//System.out.println(array[startIndex] + ", " + array[endIndex]);
startIndex++;
endIndex--;
count++;
} else if (argNumberToFind > sum) {
startIndex++;
} else {
endIndex--;
}
}
System.out.println("\nPairs found: " + count + "\n");
}
然后得到:
排序和查找对:38 查找对:4405
寻找配对的时间炸毁了!排序和查找与我们之前看到的保持一致。
因此,最有可能解决您的问题的方法是编译器正在优化某些东西,而几乎为空的for循环肯定是编译器可以用来优化的东西。而对于排序和查找,复杂的逻辑可能会使优化器后退。找到您的算法课程。在这里,java在耍你。
您可以尝试的另一件事是使用不同的语言。我很确定您这样做会发现有趣的东西!
答案 2 :(得分:0)
因为使用选择算法,所以调用Array.sort(MyArray)方法需要很长时间。这意味着,Sort方法将遍历所有数组x次(x = array.lenght),以查找最小/最大值,并将其设置在数组顶部,依此类推。
这就是为什么使用此方法需要很长时间,具体取决于数组大小。
答案 3 :(得分:0)
我从您的 sortAndFindPairsOfNumbers 方法中删除了所有内容,只是保留了
Arrays.sort(array);
但是时差仍然更大。 这意味着大部分时间是通过排序方法进行的。
因此,您认为第二种方法有效的想法是不正确的。其全部与输入大小有关。
如果您保留numberRange,假设为 1000 ,那么 SortAndFindPairs 将会更快。