为什么二次时间算法比线性时间算法执行得更快
Why would a quadratic time algorithm execute faster than a linear time algorithm
这里有两种不同的解决方案来寻找 "Number of subarrays having product less than K",一种的运行时间为 O(n),另一种为 O(n^2)。但是,具有 O(n^2) 的执行速度比具有线性运行时复杂度的执行速度快大约 4 倍(1 秒对 4 秒)。有人能解释一下为什么会这样吗?
具有 O(n) 运行时间的解决方案 1:
static long countProductsLessThanK(int[] numbers, int k)
{
if (k <= 1) { return 0; }
int prod = 1;
int count = 0;
for (int right = 0, left = 0; right < numbers.length; right++) {
prod *= numbers[right];
while (prod >= k)
prod /= numbers[left++];
count += (right-left)+1;
}
return count;
}
O(n^2) 运行时间的解决方案 2:
static long countProductsLessThanK(int[] numbers, int k) {
long count = 0;
for (int i = 0; i < numbers.length; i++) {
int productSoFar = 1;
for (int j = i; j < numbers.length; j++) {
productSoFar *= numbers[j];
if (productSoFar >= k)
break;
count++;
}
}
return count;
}
示例主程序:
public static void main(String[] args) {
int size = 300000000;
int[] numbers = new int[size];
int bound = 1000;
int k = bound/2;
for (int i = 0; i < size; i++)
numbers[i] = (new Random().nextInt(bound)+2);
long start = System.currentTimeMillis();
System.out.println(countProductLessThanK(numbers, k));
System.out.println("O(n) took " + ((System.currentTimeMillis() - start)/1000) + "s");
start = System.currentTimeMillis();
System.out.println(countMyWay(numbers, k));
System.out.println("O(n^2) took " + ((System.currentTimeMillis() - start)/1000) + "s");
}
编辑 1:
我在示例测试程序中选择的数组大小有 300,000,000 个元素。
编辑2:
数组大小:300000000:
O(n) 耗时 4152 毫秒
O(n^2) 耗时 1486 毫秒
数组大小:100000000:
O(n) 耗时 1505 毫秒
O(n^2) 耗时 480 毫秒
数组大小:10000:
O(n) 耗时 2 毫秒
O(n^2) 耗时 0 毫秒
您选择的数字在[2, 1001]范围内均匀分布,您计算的是乘积小于500的子数组。找到大子数组的概率基本为0;其乘积小于 500 的最长可能子数组的长度为 8,并且只有九个可能的子数组具有该长度(全部为 2,以及八个包含七个 2 和一个 3 的数组);击中其中一个的可能性微乎其微。一半的数组值已经超过 500;在给定起点找到长度为 2 的子数组的概率小于四分之一。
所以你的理论上 O(n²) 算法与这个测试是有效线性的。而你的 O(n) 算法需要在每个点进行除法,这真的很慢;对于 n
.
的小值,比 n
乘法慢
在第一个中,您要进行除法(慢)、乘法和多次求和。
在第二个中,较重的操作是乘法,正如第一个答案所说,该算法对于您的测试用例是有效线性的。
这里有两种不同的解决方案来寻找 "Number of subarrays having product less than K",一种的运行时间为 O(n),另一种为 O(n^2)。但是,具有 O(n^2) 的执行速度比具有线性运行时复杂度的执行速度快大约 4 倍(1 秒对 4 秒)。有人能解释一下为什么会这样吗?
具有 O(n) 运行时间的解决方案 1:
static long countProductsLessThanK(int[] numbers, int k)
{
if (k <= 1) { return 0; }
int prod = 1;
int count = 0;
for (int right = 0, left = 0; right < numbers.length; right++) {
prod *= numbers[right];
while (prod >= k)
prod /= numbers[left++];
count += (right-left)+1;
}
return count;
}
O(n^2) 运行时间的解决方案 2:
static long countProductsLessThanK(int[] numbers, int k) {
long count = 0;
for (int i = 0; i < numbers.length; i++) {
int productSoFar = 1;
for (int j = i; j < numbers.length; j++) {
productSoFar *= numbers[j];
if (productSoFar >= k)
break;
count++;
}
}
return count;
}
示例主程序:
public static void main(String[] args) {
int size = 300000000;
int[] numbers = new int[size];
int bound = 1000;
int k = bound/2;
for (int i = 0; i < size; i++)
numbers[i] = (new Random().nextInt(bound)+2);
long start = System.currentTimeMillis();
System.out.println(countProductLessThanK(numbers, k));
System.out.println("O(n) took " + ((System.currentTimeMillis() - start)/1000) + "s");
start = System.currentTimeMillis();
System.out.println(countMyWay(numbers, k));
System.out.println("O(n^2) took " + ((System.currentTimeMillis() - start)/1000) + "s");
}
编辑 1:
我在示例测试程序中选择的数组大小有 300,000,000 个元素。
编辑2:
数组大小:300000000:
O(n) 耗时 4152 毫秒
O(n^2) 耗时 1486 毫秒
数组大小:100000000:
O(n) 耗时 1505 毫秒
O(n^2) 耗时 480 毫秒
数组大小:10000:
O(n) 耗时 2 毫秒
O(n^2) 耗时 0 毫秒
您选择的数字在[2, 1001]范围内均匀分布,您计算的是乘积小于500的子数组。找到大子数组的概率基本为0;其乘积小于 500 的最长可能子数组的长度为 8,并且只有九个可能的子数组具有该长度(全部为 2,以及八个包含七个 2 和一个 3 的数组);击中其中一个的可能性微乎其微。一半的数组值已经超过 500;在给定起点找到长度为 2 的子数组的概率小于四分之一。
所以你的理论上 O(n²) 算法与这个测试是有效线性的。而你的 O(n) 算法需要在每个点进行除法,这真的很慢;对于 n
.
n
乘法慢
在第一个中,您要进行除法(慢)、乘法和多次求和。
在第二个中,较重的操作是乘法,正如第一个答案所说,该算法对于您的测试用例是有效线性的。