如何确定一组int上合并排序的可能运行时间?

时间:2014-12-14 21:02:18

标签: java arrays algorithm sorting big-o

我正在测试一些排序算法的运行时间,我在一个大小为1,280,000的数组上测试了这个算法,并且运行时间约为0.246秒。我现在试图找出我的算法在一个双倍大小(2,560,000)的数组上的理论运行时间。我试图弄清楚如何根据合并排序的大O来计算运行时间,即nlog(n)。我将.246插入nlogn算法,但得出了一个负数。谷歌和其他堆栈溢出问题并没有完全帮助。我的mergeSort工作正常,但我在下面附加了代码。感谢任何帮助,提前谢谢!

/**
 * This is another sorting algorithm where the data array is first split
 * into two, then recursively sorted, at each recursive level the method
 * will merge the current two variables together, and by the time the method
 * reaches the root call the array will be sorted.
 * @param data: The array that needs to be sorted.
 * @param first: The starting index of the sort.
 * @param n : The ending index of the sort.
 */
public static void mergeSort(int[] data, int first, int n) {

    if (data.length < 2) {
        return;
    }
    int n1;//first element of first half
    int n2;//first element of the second half
    if (n > 1) {
        //figure out the size of the array
        n1 = n / 2;
        n2 = n - n1;

        mergeSort(data, first, n1);
        mergeSort(data, first + n1, n2);

        //now merge the two halves
        merge(data, first, n1, n2);
    }

}

private static void merge(int[] data, int first, int n1, int n2) {
    int[] temp = new int[n1 + n2];
    int copied = 0;
    int copied1 = 0;
    int copied2 = 0;
    int i;

    while ((copied1 < n1) && (copied2 < n2)) {
        if (data[first + copied1] < data[first + n1 + copied2]) {
            temp[copied++] = data[first + (copied1++)];
        } else {
            temp[copied++] = data[first + n1 + (copied2++)];
        }
    }
    //make sure copied1 is completely transferred over
    while (copied1 < n1) {
        temp[copied++] = data[first + (copied1++)];
    }
    //copy temp into data to complete the process
    for (i = 0; i < copied; i++) {
        data[first + i] = temp[i];
    }

}

2 个答案:

答案 0 :(得分:1)

“理论”中合并排序是一种复杂度为O(n.log(n))的算法。
这是我们都知道的一个事实,但是:实际上有很多因素对我们起作用。 即内存限制,CPU过载以及Java Heap。

假设您已在没有边界的计算机上运行代码:

=

  

0.246 = alpha * n * log(n)
  其中n = 1,280,000,alpha是我们的机器过程因子

     

0.246 = alpha * 1.28E + 6 * log(1.28E6)    - &GT; alpha = 0.246 /(1.28E6 * log(1.28E6))    - &GT; alpha = 3.14689524e-8

现在让我们用计算出的alpha替换数字,n = 2,560,000:

  

估计值= 3.14689524e-8 * 2.56E6 * log(2.56E6)    - &GT;估计= 0.51625113199

所以大概需要0.516秒。

注意:这仅在您拥有无限资源且没有后台进程时才有效。

答案 1 :(得分:0)

您的测试尺寸很小。

您需要多次重复实验并使用许多不同的尺寸,以便制作不同时间消耗图的图表。

原因:在物理处理器上,计算机使用缓存:快速内存,以便最佳地使用处理器。程序完全加载到缓存之前需要一段时间。

这意味着第一次运行程序时,由于部分时间浪费在加载程序上,因此需要更多时间。这称为冷启动。第二次,人们可以从之前运行的早期工作中受益。这称为热启动

此外,运行时间也往往取决于外部因素。假设您运行该程序,但同时网络上发生了某些事情或您插入了USB设备。在这种情况下,操作系统将暂停执行,并首先为事件进行一些记录。簿记可以持续几毫秒,因此对单次运行产生重大影响。

此外,您需要尝试不同的数组。排序数组的排序通常比未排序数组快。这是因为许多merge实现使用swapping(以提高性能),因此执行更少的交换。

要结束:您应该使用不同的数组,不同的大小,并经常重复实验,以平衡缓存等方面。

鉴于您这样做,您可以按如下方式确定运行时间。由于时间复杂度为 O(n log n),这意味着函数采用以下形式:

a*n log n+b*n+c*log n+d

您可以将此公式插入Least squares方法的 Vandermonde矩阵

[  1    log(n_1)    n_1    n_1*log(n_1)  ]                 [ t_1 ]
[  1    log(n_2)    n_2    n_1*log(n_2)  ]      [ d ]      [ t_2 ]
[  1    log(n_3)    n_3    n_1*log(n_3)  ]      [ c ]      [ t_3 ]
[  1    log(n_4)    n_4    n_1*log(n_4)  ]   x  [ b ]  =   [ t_4 ]
[  1    log(n_5)    n_5    n_1*log(n_5)  ]      [ a ]      [ t_5 ]
[  1      ...       ...        ...       ]                 [ ... ]
[  1    log(n_m)    n_m    n_1*log(n_m)  ]                 [ t_m ]

或矩阵表示法:

X * w = t

使用n_i实验i的数组大小和t_i对此数组进行排序所需的时间。

然后,您可以通过计算确定常数:

w = (X^T*X)^-1*X*t

您可以使用 GNU / Octave 来执行此操作。

然后,您可以使用最小二乘法的方法派生abcd。这应该给出时间的良好近似值。

如果它们差异太大,您的实施就会出现问题。如果a(接近)为零,则您的算法很可能表现为sub O(n log n),并且如果函数没有跟上数据点的话,行为更强(因此 O(n ^ 2)