为什么二次时间算法比线性时间算法执行得更快

时间:2018-06-26 21:56:07

标签: java algorithm greedy

这里有两种不同的解决方案,用于查找“乘积小于K的子数组的数量”,一种具有运行时O(n),另一种具有O(n ^ 2)。但是,O(n ^ 2)的执行速度比线性运行时复杂度(1s对4s)的执行速度快约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");
}

Edit1:

我在示例测试程序中选择的数组大小有300,000,000个元素。

Edit2:

数组大小:300000000:

O(n)花了4152ms

O(n ^ 2)耗时1486ms

数组大小:100000000:

O(n)花了1505毫秒

O(n ^ 2)用了480ms

数组大小:10000:

O(n)花费了2ms

O(n ^ 2)花费了0ms

2 个答案:

答案 0 :(得分:8)

您选择的数字均匀分布在[2,1001]范围内,并且您要对乘积小于500的子数组进行计数。发现大子数组的可能性本质上为0;但是,如果要对这些子数组进行计数,则结果为0。乘积小于500的可能的最长子数组长度为8,只有9个具有该长度的子数组(全为2,八个数组为7个2和3);击中其中之一的可能性很小。一半的数组值已经超过500;在给定的起始点发现甚至两个长度的子数组的概率也不到四分之一。

因此,从理论上讲,您的O(n²)算法在该测试中实际上是线性的。而且您的O(n)算法需要在每个点进行除法,这确实很慢;对于较小的n,速度比n慢。

答案 1 :(得分:0)

在第一个中,您正在除(慢),相乘并进行多次求和。

在第二个中,较重的运算是乘法,并且如第一个答案所述,该算法对于您的测试用例实际上是线性的。