Algorithm 求O(n)时间内nxn矩阵的局部极小值

Algorithm 求O(n)时间内nxn矩阵的局部极小值,algorithm,search,Algorithm,Search,所以,这不是我的家庭作业问题,而是取自coursera算法和数据结构课程(现已完成)的未评分家庭作业 您将得到一个由n个不同数字组成的n×n网格。如果一个数小于它的所有相邻数,则它是一个局部最小值。(一个数字的邻域是紧挨着上面、下面、左边或右边的一个。大多数数字有四个邻域;边上的数字有三个;四个角上有两个。)使用分治算法设计范例计算局部最小值,只需对数对进行O(n)比较。(注意:由于输入中有n2个数字,因此您无法查看所有这些数字。提示:请考虑哪些类型的复发将为您提供所需的上限。) 由于这些数字不

所以,这不是我的家庭作业问题,而是取自coursera算法和数据结构课程(现已完成)的未评分家庭作业

您将得到一个由n个不同数字组成的n×n网格。如果一个数小于它的所有相邻数,则它是一个局部最小值。(一个数字的邻域是紧挨着上面、下面、左边或右边的一个。大多数数字有四个邻域;边上的数字有三个;四个角上有两个。)使用分治算法设计范例计算局部最小值,只需对数对进行O(n)比较。(注意:由于输入中有n2个数字,因此您无法查看所有这些数字。提示:请考虑哪些类型的复发将为您提供所需的上限。)


由于这些数字不是按任何顺序排列的,除了O(n2)比较外,我看不出我们还能做什么。

我认为这其实很容易


将问题转化为三维问题,看看算法为何有效。把矩阵放在桌子上。假设每个单元格中都有支柱,支柱的高度与其值成正比。把球放在任何柱子上。让球始终落在相邻的最低高度的柱子上,直到它达到当地的最低高度。

我们可以通过观察它是如何出错的来调整类似Jared的答案这样的词

这个答案中的想法——这是一个很好的答案——是“滚下山”。这只是意味着,如果你在一个元素上,检查它是否是一个局部最小值。如果是这样,你就完了;否则,请转到其最近邻居中最小的一个。最终这必须终止,因为每一步都是到一个更小的元素,而这不可能在有限数组中永远持续下去

这种方法的问题在于“滚动”可能会在整个地方弯曲:

20 100 12  11 10 100  2
19 100 13 100  9 100  3
18 100 14 100  8 100  4
17  16 15 100  7   6  5
如果从左上角开始“滚下坡”,将访问阵列中大约一半的元素。这太多了,所以我们必须限制它一点

首先检查中间的列和中间的行。在所有这些元素中找到最小的元素并从那里开始

从那里滚一步“下坡”,进入四个象限之一。你会进入一个象限,因为中间列和/或行中的相邻元素较大,所以只有两个相邻象限中的一个可以是“下坡”。 现在考虑一下如果你从那里滚下坡会发生什么。显然,你最终会达到一个局部最小值。(我们实际上不会这样做,因为这会花费太长时间。)但是,在滚动的过程中,您永远不会离开该象限。。。因为要这样做,您必须穿过中间的列或中间的行,并且这些元素中没有一个比您开始的位置小。因此,该象限包含某个地方的局部最小值

因此,在线性时间内,我们已经确定了一个象限,它必须包含一个局部极小值,我们已经将n减半。现在只需重复

这个算法需要时间2n+2n/2+2n/4+…,等于4n,也就是O(n),所以我们完成了

有趣的是,我们根本没有使用“滚动下坡”,除了关键部分:证明算法有效

[更新]

因为,这个答案并不完全正确,因为在你“只是递归”之后,你可能会再次滚出象限


最简单的解决方法是在“下山”之前,在中间行、中间列和边界中找到最小的元素。

Nemo接受的答案很好,但并不完全正确:

因此,在线性时间内,我们已经确定了一个象限,它必须包含一个局部极小值,我们已经将n减半。现在只需重复

我指的是“只是递归”位。问题是我们不能直接这样做,因为在下一次迭代中,我们可能会找到一个局部最小值,它不是原始网格的局部最小值(下面的x表示一些任意大的数字):

在第一次迭代中,我们发现10是中间行和中间列的最小值。我们向左走(因为1小于10)。所以我们的下一个迭代是在左上象限。但现在中间行和列的最小值将是31(若象限的边界被认为是其中的一部分,则为30)。然后,您将得出结论,这是一个局部最小值。但这并不是为了整个网格

我们可以用各种方法纠正这一不幸的缺陷。我是这样解决的:

在每次迭代中,除了网格本身,我们还跟踪当前最小候选点(即第一次迭代后的上例中的1;在初始状态下,我们可以说最小候选点为正无穷大)。我们计算中间行和列的最小值,并将其与最小候选值进行比较。如果后者较小,我们递归到包含最小候选的象限。否则,我们会忘记前面的候选项,然后才检查新的中间行/列最小值是否实际上是局部最小值。若并没有,那个么像往常一样递归到我们从它向下倾斜的任何象限(并跟踪新的最小候选值)

或者,您可以按照中所述修改过程:在每次迭代中,您可以查看中行/中列和网格边界,而不是查看中行/中列。那么算法再次是正确的


你可以选择你喜欢的方式。

好吧,这就是你如何分而治之的方式

1) 将n x n矩阵划分为四个n/2 x n/2子矩阵

2) 继续递归地划分子矩阵,直到得到一个2x2矩阵为止

3) 检查2 x 2矩阵的任何元素是否为局部最小值

递推方程为:T(n)=4*T(n/2)+O(1)

4*T(n/2)表示4个n/2 x n/2子矩阵,O(1)表示检查2 x 2子矩阵是否具有局部最小值

主定理说这是一个O(n^2)最坏情况的界

但是我认为我们可以得到一个最好的例子
 x  x 39  x  x 50  x  x  x  x  x
 x  x 38  x  x 49  x  x  x  x  x
37 36 33 34 35 48  x  x  x  x  x
 x  x 32  x  1 10  x  x  x  x  x
 x  x 31  x  x 47  x  x  x  x  x
46 45 30 44 43 60 51 52 53 54 55
 x  x  2  x  x 56  x  x  x  x  x
 x  x  x  x  x 57  x  x  x  x  x
 x  x  x  x  x 58  x  x  x  x  x
 x  x  x  x  x 59  x  x  x  x  x
private void FindlocalMin(matrix,rowIndex,colIndex,width){
    if(width == 1){ checkForLocalMinimum(matrix,rowIndex,colIndex); return;} //2x2 matrix
    FindlocalMin(matrix,rowIndex,colIndex,width/2);  
    FindlocalMin(matrix, (rowIndex + (width/2) + 1) ,colIndex,width/2);
    FindlocalMin(matrix,rowIndex, (colIndex + (width/2) + 1) ,width/2);
    FindlocalMin(matrix,(rowIndex + (width/2) + 1), (colIndex + (width/2) + 1) ,width/2);
}

private void checkForLocalMinimum(.........){
    if(found Local Minimum in 2x2 matrix){ exit recursion stack;} 
}
private static int findLocalMinimum(int[][] matrix, int rowStart, int rowEnd, int colStart, int colEnd) {

    int midRow = (rowStart + rowEnd) / 2;
    int minPos = findMin(matrix, midRow, colStart, colEnd);

    if (minPos >= (colStart + colEnd) / 2)
        colStart = (colStart + colEnd) / 2;
    else
        colEnd = (colStart + colEnd) / 2;

    if (matrix[midRow][minPos] < matrix[midRow + 1][minPos]
            && matrix[midRow][minPos] < matrix[midRow - 1][minPos]) {
        return matrix[midRow][minPos];
    } else if (matrix[midRow][minPos] > matrix[midRow + 1][minPos]) {
        return findLocalMinimum(matrix, midRow, rowEnd, colStart, colEnd);
    } else {
        return findLocalMinimum(matrix, rowStart, midRow, colStart, colEnd);
    }

}

private static int findMin(int[][] matrix, int midRow, int colStart, int colEnd) {
    int min = Integer.MAX_VALUE;
    int pos = -1;

    for (int i = colStart; i < colEnd; i++) {
        if (matrix[midRow][i] < min) {
            min = matrix[midRow][i];
            pos = i;
        }

    }

    return pos;
}

public static void main(String[] args) {
    // Best Case
    /*
     * int[][] matrix= { {1,-2,4,-6,1,8}, {-3,-6,-8,8,1,3}, {1,2,6,-2,-8,-6},
     * {-2,9,6,3,0,9}, {9,-1,-7,1,2,-6}, {-9,0,8,7,-6,9} };
     */

    // Two Iteration Down Case
    /*
     * int[][] matrix= { { 1,-2, 4,-6, 1, 8}, {-3,-6,-8, 8, 1, 3}, { 1, 2, 6, 9, 0,
     * 6}, {-2, 9, 6,-1,-1, 9}, { 9,-1,-7, 1, 2,-6}, {-9, 0, 8, 7,-6, 9} };
     */

    /*
     * //Left Down Case int[][] matrix= { { 1,-2, 4,-6, 0, 8}, {-3,-6,-8, 8,-2, 3},
     * {-2, 9, 6,-1, 1, 9}, { 1, 0, 6, 9, 2, 6}, { 9,-1,-7, 1, 2,-6}, {-9, 0, 8,
     * 7,-6, 9} };
     */

    int[][] matrix = { { 1, -2, 4, }, { -3, -6, -8, }, { -2, 9, 6, }

    };

    System.out.println(findLocalMinimum(matrix, 0, matrix.length, 0, matrix.length));

}