二维最大子数组

发布于 2024-10-27 02:17:47 字数 268 浏览 2 评论 0原文

Bentley 的Programming Pearls(第二版)在有关最大子数组问题的章节中描述了其二维版本:

...我们有一个 n × n 实数数组,我们必须找到任何矩形子数组中包含的最大和。这个问题的复杂性是多少?

Bentley 提到,截至该书出版之日(2000 年),寻找最佳解决方案的问题尚未解决。
现在还是这样吗?哪个是最著名的解决方案?有最近的文献吗?

Bentley's Programming Pearls (2nd ed.), in the chapter about the maximum subarray problem, describes its two-dimensional version:

...we are given an n × n array of reals, and we must find the maximum sum contained in any rectangular subarray. What is the complexity of this problem?

Bentley mentions that, as of the book's publication date (2000), the problem of finding an optimal solution was open.
Is it still so? Which is the best known solution? Any pointer to recent literature?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

何止钟意 2024-11-03 02:17:47

这个问题(最大子数组)的一维解决方案是 Theta(n),使用一种名为“Kadane 算法”的算法(我确信还有其他算法,但我对此有个人经验)。使用 Kadane 算法的实现,该问题的 2D 解决方案(最大子矩形)已知为 O(n^3)(我再次确定还有其他算法,但我以前使用过这个算法)。

虽然我们知道 Theta(n^3) 可以找到二维解,但没有人能够证明 n^3 是否是下界。对于许多算法来说,当增加维度时,算法的下限就会增加一个常数值。对于这个特定的问题,复杂性不会线性增加,因此没有已知的解决方案适用于任何给定的维度,因此问题仍然悬而未决。

参考类似的情况:我们知道2个矩阵可以在n^3次内相乘。我相信还有一种算法可以在 n^2.8 中做到这一点。然而,没有数学表明我们不能让它低于 n^2.8,所以它仍然是一个“开放”算法。

The 1D solution to this problem (the maximal sub-array) is Theta(n) using an algorithm called "Kadane's Algorithm" (there are other algorithms I'm sure, but I have personal experience with this one). The 2D solution to this problem (the maximal sub-rectangle) is known to be O(n^3) using an implementation of Kadane's Algorithm (again I'm sure there's others, but I've used this before).

Although we know that the 2D solution can be found in Theta(n^3), no one has been able to prove whether or not n^3 is the lower bound. For many algorithms, when you increase a dimension you increase the lower bound on the algorithm by a constant magnitude. With this particular problem the complexity doesn't increase linearly, and therefore there is no known solution to work for any given dimension, thus the problem is still open.

In reference to a similar case: we know that 2 matrices can be multiplied together in n^3 time. There is also an algorithm out there that can do it in n^2.8 I believe. However, there is no math indicating that we can't get it lower than n^2.8, so it's still an "open" algorithm.

柠栀 2024-11-03 02:17:47
   // Program to find maximum sum subarray in a given 2D array
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define ROW 4
#define COL 5

  // Implementation of Kadane's algorithm for 1D array. The function returns the
// maximum sum and stores starting and ending indexes of the maximum sum subarray
// at addresses pointed by start and finish pointers respectively.
int kadane(int* arr, int* start, int* finish, int n)
{
// initialize sum, maxSum and
int sum = 0, maxSum = INT_MIN, i;

// Just some initial value to check for all negative values case
*finish = -1;

// local variable
int local_start = 0;

for (i = 0; i < n; ++i)
{
    sum += arr[i];
    if (sum < 0)
    {
        sum = 0;
        local_start = i+1;
    }
    else if (sum > maxSum)
    {
        maxSum = sum;
        *start = local_start;
        *finish = i;
    }
}

 // There is at-least one non-negative number
if (*finish != -1)
    return maxSum;

// Special Case: When all numbers in arr[] are negative
maxSum = arr[0];
*start = *finish = 0;

// Find the maximum element in array
for (i = 1; i < n; i++)
{
    if (arr[i] > maxSum)
    {
        maxSum = arr[i];
        *start = *finish = i;
    }
   }
   return maxSum;
 }

 // The main function that finds maximum sum rectangle in M[][]
  void findMaxSum(int M[][COL])
 {
 // Variables to store the final output
 int maxSum = INT_MIN, finalLeft, finalRight, finalTop, finalBottom;

 int left, right, i;
 int temp[ROW], sum, start, finish;

  // Set the left column
   for (left = 0; left < COL; ++left)
  {
     // Initialize all elements of temp as 0
    memset(temp, 0, sizeof(temp));

    // Set the right column for the left column set by outer loop
    for (right = left; right < COL; ++right)
    {
        // Calculate sum between current left and right for every row 'i'
        for (i = 0; i < ROW; ++i)
            temp[i] += M[i][right];

        // Find the maximum sum subarray in temp[]. The kadane() function
        // also sets values of start and finish.  So 'sum' is sum of
        // rectangle between (start, left) and (finish, right) which is the
        //  maximum sum with boundary columns strictly as left and right.
        sum = kadane(temp, &start, &finish, ROW);

        // Compare sum with maximum sum so far. If sum is more, then update
        // maxSum and other output values
        if (sum > maxSum)
        {
            maxSum = sum;
            finalLeft = left;
            finalRight = right;
            finalTop = start;
            finalBottom = finish;
        }
     }
   }

  // Print final values
  printf("(Top, Left) (%d, %d)\n", finalTop, finalLeft);
  printf("(Bottom, Right) (%d, %d)\n", finalBottom, finalRight);
  printf("Max sum is: %d\n", maxSum);
  }

  // Driver program to test above functions
  int main()
  {
  int M[ROW][COL] = {{1, 2, -1, -4, -20},
                   {-8, -3, 4, 2, 1},
                   {3, 8, 10, 1, 3},
                   {-4, -1, 1, 7, -6}
                  };

  findMaxSum(M);

 return 0;
 }


  ////////I found this program , hope it will help you
   // Program to find maximum sum subarray in a given 2D array
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define ROW 4
#define COL 5

  // Implementation of Kadane's algorithm for 1D array. The function returns the
// maximum sum and stores starting and ending indexes of the maximum sum subarray
// at addresses pointed by start and finish pointers respectively.
int kadane(int* arr, int* start, int* finish, int n)
{
// initialize sum, maxSum and
int sum = 0, maxSum = INT_MIN, i;

// Just some initial value to check for all negative values case
*finish = -1;

// local variable
int local_start = 0;

for (i = 0; i < n; ++i)
{
    sum += arr[i];
    if (sum < 0)
    {
        sum = 0;
        local_start = i+1;
    }
    else if (sum > maxSum)
    {
        maxSum = sum;
        *start = local_start;
        *finish = i;
    }
}

 // There is at-least one non-negative number
if (*finish != -1)
    return maxSum;

// Special Case: When all numbers in arr[] are negative
maxSum = arr[0];
*start = *finish = 0;

// Find the maximum element in array
for (i = 1; i < n; i++)
{
    if (arr[i] > maxSum)
    {
        maxSum = arr[i];
        *start = *finish = i;
    }
   }
   return maxSum;
 }

 // The main function that finds maximum sum rectangle in M[][]
  void findMaxSum(int M[][COL])
 {
 // Variables to store the final output
 int maxSum = INT_MIN, finalLeft, finalRight, finalTop, finalBottom;

 int left, right, i;
 int temp[ROW], sum, start, finish;

  // Set the left column
   for (left = 0; left < COL; ++left)
  {
     // Initialize all elements of temp as 0
    memset(temp, 0, sizeof(temp));

    // Set the right column for the left column set by outer loop
    for (right = left; right < COL; ++right)
    {
        // Calculate sum between current left and right for every row 'i'
        for (i = 0; i < ROW; ++i)
            temp[i] += M[i][right];

        // Find the maximum sum subarray in temp[]. The kadane() function
        // also sets values of start and finish.  So 'sum' is sum of
        // rectangle between (start, left) and (finish, right) which is the
        //  maximum sum with boundary columns strictly as left and right.
        sum = kadane(temp, &start, &finish, ROW);

        // Compare sum with maximum sum so far. If sum is more, then update
        // maxSum and other output values
        if (sum > maxSum)
        {
            maxSum = sum;
            finalLeft = left;
            finalRight = right;
            finalTop = start;
            finalBottom = finish;
        }
     }
   }

  // Print final values
  printf("(Top, Left) (%d, %d)\n", finalTop, finalLeft);
  printf("(Bottom, Right) (%d, %d)\n", finalBottom, finalRight);
  printf("Max sum is: %d\n", maxSum);
  }

  // Driver program to test above functions
  int main()
  {
  int M[ROW][COL] = {{1, 2, -1, -4, -20},
                   {-8, -3, 4, 2, 1},
                   {3, 8, 10, 1, 3},
                   {-4, -1, 1, 7, -6}
                  };

  findMaxSum(M);

 return 0;
 }


  ////////I found this program , hope it will help you
能否归途做我良人 2024-11-03 02:17:47

仅供参考,新版的书有一个答案,但它很模糊,我不知道它会带来什么。

无论如何,我会使用分而治之+动态规划来解决这个问题。我们将 MaxSum(x, y) 定义为以 NXN 数组左上角为界的矩形内任何子数组的最大总和,高度为 y,宽度为 x。 (因此问题的答案将在 MaxSum(n-1, n-1) 中)

MaxSum(x, y) is the max between:
1) MaxSum(x, y-1)
2) MaxSum(x-1, y)
3) Array[x, y] (the number in this N X N array for this specific location)
4) MaxEnding(x, y-1) + SUM of all elements from Array[MaxEndingXStart(x, y-1), y] to Array[x, y]
5) MaxEnding(x-1, y) + SUM of all elements from Array[x, MaxEndingYStart(x-1, y)] to Array[x, y]

MaxEnding(x, y-1) 是包含 Array[x, y-1] 中的 # 的任何子数组的最大和。
同样,MaxEnding(x-1, y) 是 Array[x-1, y] 中包含 # 的任何子数组的最大和。
MaxEndingXStart(x, y-1) 是子数组的起始 x 坐标,该子数组具有包含 Array[x, y-1] 中的 # 的任何子数组的最大总和。
MaxEndingYStart (x-1, y) 是具有 Array[x-1, y] 中包含 # 的任何子数组的最大总和的子数组的起始 y 坐标。

下面#4 和#5 中的 2 个总和可以通过在遍历每一列时保留特定行中遇到的所有元素的总和来轻松计算,然后减去 2 个总和以获得特定部分的总和。

要实现这一点,您需要采用自下而上的方法,因为您需要计算 Max(x, y-1)、Max(x-1, y)、MaxEnding(x, y-1) 和 MaxEnding( x-1, y).. 因此您可以在计算 MaxEnding(x, y) 时进行查找。

//first do some preprocessing and store Max(0, i) for all i from 0 to n-1.
//and store Max(i, 0) for all i from 0 to n-1.

for(int i =1; i < n; i++){
   for(int j=1; j < n; j++) {
      //LOGIC HERE
   }
}

FYI, the new edition of the book has an answer, but it is so vague, I don't know what it would entail.

In any case, I would use divide and conquer + dynamic programming to solve this. Let's define MaxSum(x, y) as the maximum sum of any subarray inside the rectangle bounded by the top-left most corner of the N X N array, with height y and width x. (so the answer to the question would be in MaxSum(n-1, n-1))

MaxSum(x, y) is the max between:
1) MaxSum(x, y-1)
2) MaxSum(x-1, y)
3) Array[x, y] (the number in this N X N array for this specific location)
4) MaxEnding(x, y-1) + SUM of all elements from Array[MaxEndingXStart(x, y-1), y] to Array[x, y]
5) MaxEnding(x-1, y) + SUM of all elements from Array[x, MaxEndingYStart(x-1, y)] to Array[x, y]

MaxEnding(x, y-1) is the maximum sum of any subarray that INCLUDES the # in Array[x, y-1].
Likewise, MaxEnding(x-1, y) is the maximum sum of any subarray that INCLUDES the # in Array[x-1, y].
MaxEndingXStart(x, y-1) is the STARTING x coordinate of the subarray that has the maximum sum of any subarray that INCLUDEs the # in Array[x, y-1].
MaxEndingYStart (x-1, y) is the STARTING y coordinate of the subarray that has the maximum sum of any subarray that INCLUDES the # in Array[x-1, y].

The 2 sum's in #4 and #5 below can be computed easily by keeping the sum of all elements encountered in a specific row, as you go through each column, then subtracting 2 sums to get the sum for a specific section.

To implement this, you would need to do a bottom-up approach, since you need to compute Max(x, y-1), Max(x-1, y), MaxEnding(x, y-1), and MaxEnding(x-1, y).. so you can do lookups when you compute MaxEnding(x, y).

//first do some preprocessing and store Max(0, i) for all i from 0 to n-1.
//and store Max(i, 0) for all i from 0 to n-1.

for(int i =1; i < n; i++){
   for(int j=1; j < n; j++) {
      //LOGIC HERE
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文