calgorithm

How to optimize the code that finds the maximum sum of elements within a sub-rectangle (contiguous block) of a 2D array?


I have working code that finds the maximum sum of elements within a sub-rectangle of a 2D array. I'm wondering if there are any optimization techniques I could use to improve its performance.

The input is

  1. field: 2D Array
  2. fieldRow: size of array in X dimension
  3. fieldCol: size of array in Y dimension
  4. window_size: The size of window

The output

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
//TODO if max_sum already found in this windows size
int main()
{   int max_sum = INT_MIN;
    int loopCount = 0; // for performance checking

    //INPUT for 2D array field
    int field[4][5] = {
        {0, -5, 3, 4, 9},
        {10, 21, 18, -11, 0},
        {0, -2, 5, 9, 11},
        {12, 7, 8, 3, -1}};

    int fieldCol = 5; // INPUT for Y dimension of field
    int fieldRow = 4; // INPUT for X simension of field
    int window_size = 6; // INPUT for the maximum area of subrectangle

    //output 0 and exit program for window_size 0
    if (window_size <= 0)
    {
        printf("0");
        exit(0);
    }

    // incase of window_size larger than area of field
    int max_area = (fieldCol * fieldRow);
    if (window_size > max_area)
    {
        window_size = max_area;
    }

    for (int X = 0; X < fieldRow; X++) // start position of x
    {
        for (int Y = 0; Y < fieldCol; Y++) // start position of y
        {
            for (int x = X; x < fieldRow; x++) // end position of x
            {
                for (int y = Y; y < fieldCol; y++) // end position of y
                {
                    int sum = 0;
                    int count = 0;
                    //sum of all elements from starting(X,Y) to ending(x,y) position
                    for (int c_x = X; c_x <= x; c_x++)
                    {
                        for (int c_y = Y; c_y <= y; c_y++)
                        {
                            loopCount++; //benchmarking
                            sum += field[c_x][c_y];
                            count++; //count for subrectangle area
                            if(count>window_size){
                                break;
                            }
                        }
                    }

                    //set max_sum incase of the area of subrectangle is lower or equal to window_size
                    if (sum > max_sum && count <= window_size)
                    {
                        max_sum = sum;
                    }
                }
            }
        }
    }

    printf("%d", max_sum);
    printf("\nloop count %d", loopCount);

    return 0;
}

Solution

  • The algorithm can be optimized by storing a 2-D array of cumulative sums. For efficiency, include an initial row containing all zeros, and an initial column containing all zeros, and fill in the remaining elements of the array as described below:

    +---+---+---+---+---+---+
    | 0 | 0 | 0 | 0 | 0 | 0 |
    +---+---+---+---+---+---+
    | 0 |   |   |   |   |   |
    +---+---+---+---+---+---+
    | 0 |   |   |   |   |   |
    +---+---+---+---+---+---+
    | 0 |   |   |   | A | B |
    +---+---+---+---+---+---+
    | 0 |   |   |   | C | X |
    +---+---+---+---+---+---+
    

    In the above, the initial row and column have been set to all zeros, A, B, and C, contain the sum of the elements of the subrectangle from the top left corner up to and including the element. To fill in the element X, note that B contains the sum of all the elements above it, C contains the sum of all the elements to its left, and A contains the sum of all the elements that are both above it AND to its left. So the cumulative sum so far is B + C - A, and the value of X is the cumulative sum so far plus the input value to be added to the cumulative sum (B + C - A + input).

    The given input array and its cumulative sum array are shown below:

                           +---+---+---+---+---+---+
                           | 0 | 0 | 0 | 0 | 0 | 0 |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    | 0 | -5| 3 | 4 | 9 |  | 0 | 0 | -5| -2| 2 | 11|
    +---+---+---+---+---+  +---+---+---+---+---+---+
    | 10| 21| 18|-11| 0 |  | 0 | 10| 26| 47| 40| 49|
    +---+---+---+---+---+  +---+---+---+---+---+---+
    | 0 | -2| 5 | 9 | 11|  | 0 | 10| 24| 50| 52| 72|
    +---+---+---+---+---+  +---+---+---+---+---+---+
    | 12| 7 | 8 | 3 | -1|  | 0 | 22| 43| 77| 82|101|
    +---+---+---+---+---+  +---+---+---+---+---+---+
           INPUT                CUMULATIVE SUMS
    

    The sum of the elements of any subrectangle can be found with a bit of arithmetic:

    ·            ·     +------------+     +------+     ·     +------------+     +------+     ·
                       |            |     |      |           |            |     |      |
                       |            |     |      |           |            |     |      |
    ·      +-----+  =  |            |  -  |      |     ·  -  +------------+  +  +------+     ·
           |     |     |            |     |      |
           |     |     |            |     |      |
    ·      +-----+     +------------+     +------+     ·     ·      ·     ·     ·      ·     ·
    

    In words, sum of elements of the subrectangle equals the sum of elements up to the bottom right corner of the subrectangle minus the sum of elements to the left of the subrectangle minus the sum of elements above the subrectangle plus the sum of elements that are both to the left and above the subrectangle.

    For example:

                           +---+---+---+---+---+---+
                           |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   |   |   |   |   |  |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   |   |   |   |   |  |   |   | 26|   | 40|   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   |   | 5 | 9 |   |  |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   |   | 8 | 3 |   |  |   |   | 43|   | 82|   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
           INPUT                CUMULATIVE SUMS
    
     5 + 9 + 8 + 3 = 25      82 - 43 - 40 + 26 = 25
    

    The following modified code calculates 2-D array of cumulative sums from the input, and uses it in the search for the maximum subrectangle sum for all subrectangles no larger than the window size.

    #include <stdio.h>
    #include <limits.h>
    #include <stdlib.h>
    //TODO if max_sum already found in this windows size
    int main()
    {   int max_sum = INT_MIN;
        int loopCount = 0; // for performance checking
    
        //INPUT for 2D array field
        int field[4][5] = {
            {0, -5, 3, 4, 9},
            {10, 21, 18, -11, 0},
            {0, -2, 5, 9, 11},
            {12, 7, 8, 3, -1}};
    
        int fieldCol = 5; // INPUT for Y dimension of field
        int fieldRow = 4; // INPUT for X dimension of field
        int window_size = 6; // INPUT for the maximum area of subrectangle
    
        int sums[fieldRow + 1][fieldCol + 1];
    
    
        //output 0 and exit program for window_size 0
        if (window_size <= 0)
        {
            printf("0");
            exit(0);
        }
    
        // Fill in cumulative sums array
        for (int Y = 0; Y <= fieldCol; Y++)
        {
            loopCount++; //benchmarking
            sums[0][Y] = 0;
        }
        for (int X = 1; X <= fieldRow; X++)
        {
            loopCount++; //benchmarking
            sums[X][0] = 0;
            for (int Y = 1; Y <= fieldCol; Y++)
            {
                loopCount++; //benchmarking
                sums[X][Y] = sums[X - 1][Y] - sums[X - 1][Y - 1] +
                             sums[X][Y - 1] + field[X - 1][Y - 1];
            }
        }
    
        for (int X = 0; X < fieldRow; X++) // start position of x - 1
        {
            for (int Y = 0; Y < fieldCol; Y++) // start position of y - 1
            {
                for (int x = X + 1; x <= fieldRow; x++) // end position of x
                {
                    int height = x - X;
                    int area = 0;
    
                    if (height > window_size)
                    {
                        // area of width 1 subrectangle is too large
                        break;
                    }
                    for (int y = Y + 1; y <= fieldCol; y++) // end position of y
                    {
                        int sum;
    
                        loopCount++; //benchmarking
                        area += height;
                        if (area > window_size)
                        {
                            // area of subrectangle is too large
                            break;
                        }
    
                        //sum of all elements from starting(X+1,Y+1) to ending(x,y) position
                        sum = sums[x][y] - sums[X][y] + sums[X][Y] - sums[x][Y];
                        //set max_sum incase of the area of subrectangle is lower or equal to window_size
                        if (sum > max_sum)
                        {
                            max_sum = sum;
                        }
                    }
                }
            }
        }
    
        printf("%d\n", max_sum);
        printf("loop count %d\n", loopCount);
    
        return 0;
    }
    

    Output:

    57
    loop count 165
    

    The above loop count includes the construction of the sums array from the field array. If the input element values are being read in from an external stream, there is no need to store them in the field array, because they can be read in on the fly as the sums array is being constructed.

    This is the location of the subrectangle no larger than the window size of 6 with the maximum element sum:

                           +---+---+---+---+---+---+
                           |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   |   |   |   |   |  |   | 0 |   | -2|   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   | 21| 18|   |   |  |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   | -2| 5 |   |   |  |   |   |   |   |   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
    |   | 7 | 8 |   |   |  |   | 22|   | 77|   |   |
    +---+---+---+---+---+  +---+---+---+---+---+---+
           INPUT                CUMULATIVE SUMS
    
        21 + 18 +
      (-2) +  5 +
         7 +  8   = 57      77 - 22 - (-2) + 0 = 57