Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 13951

Speed-up for finding an optimal partition line

$
0
0

This coding question derived from this question.

Consider an n by n grid of integers. The task is to draw a straight line across the grid so that the part that includes the top left corner sums to the largest number possible. Here is a picture of an optimal solution with score 45:

enter image description here

We include a square in the part that is to be summed if its middle is above or on the line. Above means in the part including the top left corner of the grid. (To make this definition clear, no line can start exactly in the top left corner of the grid.)

The task is to choose the line that maximizes the sum of the part that includes the top left square. The line must go straight from one side to another. The line can start or end anywhere on a side and not just at integer points.

The Python code given is:

import numpy as npimport fractionsdef best_line(grid):    n, m = grid.shape    D = [(di, dj) for di in range(-(n - 1), n) for dj in range(-(n - 1), n)]    def slope(d):        di, dj = d        if dj == 0:            return float('inf') if di <= 0 else float('-inf'), -di        else:            return fractions.Fraction(di, dj), fractions.Fraction(-1, dj)    D.sort(key=slope)    D = np.array(D, dtype=np.int64)    s_max = grid.sum()    for grid in (grid, grid.T):        left_sum = 0        for j in range(grid.shape[1]):            left_sum += grid[:,j].sum()            for i in range(grid.shape[0]):                p = np.array([i, j], dtype=np.int64)                Q = p + D                Q = Q[np.all((0 <= Q) & (Q < np.array(grid.shape)), axis=1)]                s = left_sum                 for q in Q:                    if not np.any(q):                        break                    if q[1] <= j:                        s -= grid[q[0],q[1]]                    else:                        s += grid[q[0],q[1]]                    s_max = max(s_max, s)    return s_max

This code is already slow for n=30.

Is there any way to speed it up in practice?

Test cases

As the problem is quite complicated, I have given some example inputs and outputs.

The easiest test cases are when the input matrix is made of positive (or negative) integers only. In that case a line that makes the part to sum the whole matrix (or the empty matrix if all the integers are negative) wins.

Only slightly less simple is if there is a line that clearly separates the negative integers from the non negative integers in the matrix.

Here is a slightly more difficult example with an optimal line shown. The optimal value is 14.

enter image description here

The grid in machine readable form is:

[[ 3 -1 -2 -1] [ 0  1 -1  1] [ 1  1  3  0] [ 3  3 -1 -1]]

Here is an example with optimal value 0.

enter image description here

[[-3 -3  2 -3] [ 0 -2 -1  0] [ 1  0  2  0] [-1 -2  1 -1]]

This matrix has optimal score 31:

[[ 3  0  1  3 -1  1  1  3 -2 -1] [ 3 -1 -1  1  0 -1  2  1 -2  0] [ 2  2 -2  0  1 -3  0 -2  2  1] [ 0 -3 -3 -1 -1  3 -2  0  0  3] [ 2  2  3  2 -1  0  3  0 -3 -1] [ 1 -1  3  1 -3  3 -2  0 -3  0] [ 2 -2 -2 -3 -2  1 -2  0  0  3] [ 0  3  0  1  3 -1  2 -3  0 -2] [ 0 -2  2  2  2 -2  0  2  1  3] [-2 -2  0 -2 -2  2  0  2  3  3]]

In Python/numpy, an easy way to make more test matrices is:

import numpy as npN = 30square = np.random.randint(-3, 4, size=(N, N))

Timing

N = 30np.random.seed(42)big_square = randint(-3, 4, size=(N, N))print(best_line(np.array(big_square)))

takes 1 minute 55 seconds and gives the output 57.

  • Andrej Kesely's parallel code takes 1 min 5 seconds for n=250. This is a huge improvement.

Can it be made faster still?


Viewing all articles
Browse latest Browse all 13951

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>