在O(logn)时间内找到最大值?

时间:2013-09-08 13:06:35

标签: java max divide-and-conquer

我一直认为迭代搜索是在未排序列表中查找最大值的首选方法。

这个想法相当随机,但简而言之:我相信我可以在O(logn)时间内完成任务,n是输入数组的大小。

关于合并排序的方法:分而治之。

步骤1:将findMax()任务划分为两个子任务findMax(leftHalf)findMax(rightHalf)。此划分应在O(logn)时间内完成。

第2步:合并两个最大候选人。 此步骤中的每个图层都应该保持恒定的时间O(1),并且按照上一步,O(logn)这样的图层。所以它也应该在O(1) * O(logn) = O(logn)时间内完成(原谅滥用符号)。这是错误的。每次比较都是在恒定的时间内完成的,但是有2^j/2次这样的比较(第j级的2 ^ j对候选者)。

因此,整个任务应该在 O(logn)时间内完成。 O(n)时间。

但是,当我尝试计时时,我会得到明确反映线性O(n)运行时间的结果。

  

size = 100000000 max = 0 time = 556

     

size = 200000000 max = 0 time = 1087

     

size = 300000000 max = 0 time = 1648

     

size = 400000000 max = 0 time = 1990

     

size = 500000000 max = 0 time = 2190

     

size = 600000000 max = 0 time = 2788

     

size = 700000000 max = 0 time = 3586

怎么回事?

这是代码(我保留未初始化的数组以保存预处理时间,该方法,据我测试它,准确识别未排序数组中的最大值):

public static short findMax(short[] list) {
    return findMax(list, 0, list.length);
}

public static short findMax(short[] list, int start, int end) {
    if(end - start == 1) {
        return list[start];
    }
    else {
        short leftMax = findMax(list, start, start+(end-start)/2);
        short rightMax = findMax(list, start+(end-start)/2, end);
        return (leftMax <= rightMax) ? (rightMax) : (leftMax);
    }
}

public static void main(String[] args) {
    for(int j=1; j < 10; j++) { 
        int size = j*100000000; // 100mil to 900mil
        short[] x = new short[size];
        long start = System.currentTimeMillis();
        int max = findMax(x);
        long end = System.currentTimeMillis();
        System.out.println("size = " + size + "\t\t\tmax = " + max + "\t\t\t time = " + (end - start));
        System.out.println();
    }
}

3 个答案:

答案 0 :(得分:6)

您应该计算实际发生的比较次数:

在最后一步中,在找到前n / 2个数字和最后n / 2个nubmers的最大值后,需要再进行1次比较才能找到整个数字的最大值。

在上一步中,您必须找到第一组和第二组n / 4数的最大值以及第三组和第四组n / 4数的最大值,因此您有2次比较。

最后,在递归结束时,你有n / 2组2个数字,你必须比较每一对,所以你有n / 2个比较。

当你总结它们时:

1 + 2 + 4 + ... + n / 2 = n-1 = O(n)

答案 1 :(得分:1)

您确实创建了log(n)个图层。

但是在一天结束时,你仍然可以浏览每个创建的桶的每个元素。因此,你要经历每个元素。总的来说,你仍然是O(n)

答案 2 :(得分:1)

根据Eran的回答,你已经知道你的推理有什么问题了。

但无论如何,有一个称为主定理的定理,它有助于递归函数的运行时分析。

它反映了以下等式:

T(n) = a*T(n/b) + O(n^d)

其中T(n)是大小为n的问题的运行时间。

在您的情况下,递推方程式为T(n) = 2*T(n/2) + O(1)所以a=2b=2d=0。情况就是这样,因为对于每个n大小的问题实例,将其分解为大小为n / 2(b)的2(a)个子问题,并将它们组合在O(1)= O(n ^ 0)中

主定理简单地陈述了三种情况:

如果a = b^d,则总运行时间为O(n^d*log n)

如果a < b^d,则总运行时间为O(n^d)

如果a > b^d,则总运行时间为O(n^(log a / log b))

您的案例与第三个匹配,因此总运行时间为O(n ^(log 2 / log 2))= O(n)

尝试理解这三种情况背后的原因是一个很好的练习。它们仅仅是以下情况:

1st)我们为每个递归级别执行相同数量的总工作(这是mergesort的情况),因此我们只将合并时间O(n ^ d)乘以级别数log n。 / p>

第二)我们对第二个递归级别的工作少于第一次递归,依此类推。因此,总工作基本上是最后一个合并步骤(第一个递归级别)的工作,O(n ^ d)。

3rd)我们为更深层次(您的情况)做更多工作,因此运行时间为O(递归树中的叶数)。在你的情况下,你有n个叶子用于更深的递归级别,所以O(n)。

Stanford cousera课程中有一些简短的视频非常适合解释Master Method,https://www.coursera.org/course/algo。我相信你总是可以预览课程,即使没有注册。