我正在测试一些排序算法的运行时间,我在一个大小为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];
}
}
答案 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 来执行此操作。
然后,您可以使用最小二乘法的方法派生a
,b
,c
和d
。这应该给出时间的良好近似值。
如果它们差异太大,您的实施就会出现问题。如果a
(接近)为零,则您的算法很可能表现为sub O(n log n),并且如果函数没有跟上数据点的话,行为更强(因此 O(n ^ 2))