任何人都可以在Java中提供O-notation的基本示例吗?

时间:2010-01-06 11:48:57

标签: java big-o

我试图找到o符号的限制,我想知道是否有一个简单的例子演示了对任务的启用,其中版本1与版本2相同,但版本2在更高效后工作增强

感谢

4 个答案:

答案 0 :(得分:6)

在下面的代码中,在// do something之后添加一行来突破内部循环会留下更快的函数,但仍然是一个O(n ^ 2)。

for (int i = 0; i < n; i++) {
   for (int j = 0; i < n; j++) {
        if (i == j) {
            // do something
        }
   }
}

答案 1 :(得分:3)

O表示法描述了函数的限制行为,算法的最坏情况。通常,对于同一任务比较不同的算法就足够了,比如通过该函数对算法进行排序。

我会说,对于几乎所有的算法(O(1)算法除外)),总会有一些输入数据使算法在更短的时间内终止(或者用更少的内存消耗),然后用描述的O表示法表示那个算法。

假设我们有一个类似的计数算法:

private int counter(int n) {
  int counter;
  for (int i = 0; i < 2; i++) {
    counter = 0;
    for (int i = 0; i < n; i++) {
      counter++;
    }
  }
  return counter;
}

增长是线性的,所以这个计数器的O表示法是O(n)(我只看步骤,而不是记忆)。你可以说,嘿,我们计算两次,然后写O(2n)。真正。您甚至可以编写O(2n + c)来表示我们需要一些额外的步骤(时间)来创建和初始化局部变量。

这是一个改进的实现,仍然是线性的(O(n)),但终止速度明显更快:

private int counter(int n) {
  int counter =0;
  for (int i = 0; i < n; i++) {
    counter++;
  }
  return counter;
}

两者都可以描述为O(n)以指示线性增长。这可能就足够了,例如将这些实现与该计数器的O(n ^ 2)或O(1)实现进行比较。但是为了比较'线性'版本A和B,我们需要更精确,并将第一个识别为O(2n),将第二个识别为O(n)。现在,O符号值的比较给出了预期的结果:实现B是“更好”。

答案 2 :(得分:0)

此合并排序代码在nLog(n)中起作用:

/**
 * Mergesort algorithm.
 * @param a an array of Comparable items.
 */
public static void mergeSort( Comparable [ ] a ) {
    Comparable [ ] tmpArray = new Comparable[ a.length ];
    mergeSort( a, tmpArray, 0, a.length - 1 );
}

/**
 * Internal method that makes recursive calls.
 * @param a an array of Comparable items.
 * @param tmpArray an array to place the merged result.
 * @param left the left-most index of the subarray.
 * @param right the right-most index of the subarray.
 */
private static void mergeSort( Comparable [ ] a, Comparable [ ] tmpArray,
        int left, int right ) {
    if( left < right ) {
        int center = ( left + right ) / 2;
        mergeSort( a, tmpArray, left, center );
        mergeSort( a, tmpArray, center + 1, right );
        merge( a, tmpArray, left, center + 1, right );
    }
}

/**
 * Internal method that merges two sorted halves of a subarray.
 * @param a an array of Comparable items.
 * @param tmpArray an array to place the merged result.
 * @param leftPos the left-most index of the subarray.
 * @param rightPos the index of the start of the second half.
 * @param rightEnd the right-most index of the subarray.
 */
private static void merge( Comparable [ ] a, Comparable [ ] tmpArray,
        int leftPos, int rightPos, int rightEnd ) {
    int leftEnd = rightPos - 1;
    int tmpPos = leftPos;
    int numElements = rightEnd - leftPos + 1;

    // Main loop
    while( leftPos <= leftEnd && rightPos <= rightEnd )
        if( a[ leftPos ].compareTo( a[ rightPos ] ) <= 0 )
            tmpArray[ tmpPos++ ] = a[ leftPos++ ];
        else
            tmpArray[ tmpPos++ ] = a[ rightPos++ ];

    while( leftPos <= leftEnd )    // Copy rest of first half
        tmpArray[ tmpPos++ ] = a[ leftPos++ ];

    while( rightPos <= rightEnd )  // Copy rest of right half
        tmpArray[ tmpPos++ ] = a[ rightPos++ ];

    // Copy tmpArray back
    for( int i = 0; i < numElements; i++, rightEnd-- )
        a[ rightEnd ] = tmpArray[ rightEnd ];
}

答案 3 :(得分:0)

无法反驳部分问题吗?因此,采用流畅运行的版本,使其效率更低,但不会改变原始代码的大O.例如,添加一行以使程序在执行某些工作时休眠10秒是一个恒定的时间变化,在计算big-O时会被删除,我想。在这种情况下,带有额外代码的版本将是版本1,而另一个版本是版本2,它更有效但是无关紧要。

如果有人想要更抽象的答案,因为big-O忽略低阶项和常数乘数,这些可以是在不改变方法的整体大O的情况下可以提高效率的地方。对不起,这里没有任何Java代码,但这个答案与语言无关。