在某个区域内对多维数组中的元素求和

时间:2018-03-29 04:27:26

标签: python algorithm multidimensional-array

我正在创建一个游戏,其中机器人遍历一个2D阵列的地图。 2D阵列中的每个点都有一个"宝贝"那是一些硬币。我希望能够从机器人当前位置向上,向下,向右和向左添加最多4个位置的所有元素(制作"加"符号)。所以,如果我们有一个数组:

a = [[1, 2, 3, 4]
      [5, 6, 7 ,8]
      [9, 10, 11, 12]
      [13, 14, 15, 16]

如果机器人站在a[0][0](在1位置),总和将返回1+2+3+4+5+9+13。如果他站在a[1][2](7点),它将返回(7+3)+(8)+(5+6)+(11+15)。但我希望它最多只返回4个地方。最后,我想找到机器人的最佳位置。

这是我的代码:

def the_place_to_be(a):
    maximum = 0
    for i in range(len(a)):
        # Looping through columns
        for j in range(len(a[i])):
            # Looping through rows
            sum_at_ij = a[i][j]
            for x in range(i - max_steps_in_direction(a, i, "up"), i):
                sum_at_ij += a[x][j]

            for x in range(j - max_steps_in_direction(a[i], j, "left"), j):
                sum_at_ij += a[i][x]

            for x in range(i, i + max_steps_in_direction(a, i, "down")):
                sum_at_ij += a[x+1][j]

            for x in range(j, j + max_steps_in_direction(a[i], j, "right")):
                sum_at_ij += a[i][x+1]

            if sum_at_ij >= maximum:
                maximum = sum_at_ij
                coordinates = "(" + str(i) + ", " + str(j) + ")"
    return maximum, coordinates


def max_steps_in_direction(a, idx, direction):
    if direction == "up" or direction == "left":
        return min(idx, 4)
    elif direction == "down" or direction == "right":
        return min(len(a) - idx - 1, 4)

这可能是最疯狂的时间复杂性。我正在查看整个阵列,然后循环遍历所有元素,距离机器人站立的坐标,顶部,底部,右侧和左侧方向最多四个位置。

在每一步中,我都在计算超过我的价值。有没有办法缓解这种情况?我想也许我的变量sum_at_ij可以保存。我基本上是在多列表中的每个列表中移动。在列表中的每个点,我实际上只计算与前一个坐标不同的一些值。所以,再次说,当我移动到a[2][2]或者坐标12时,我处于坐标a[2][3]或坐标11处,差异在于:

  

sum_at_22:11 + 7 + 3 + 15 + 12 + 10 + 9

     

sum_at_23:12 + 8 + 4 + 16 + 11 + 10 + 9

我正在计算总共3个新值(顶部和底部的值不同)。如果这是一个8x8矩阵,则新值将是最高值,最低值,右侧是一个新值,左侧是一个较小值。如果我保存了每个值(可能在某个hashmap中),那么也许我可以找到一些公式......说实话,我不知道。也许这是一个math.stackexchange问​​题。

任何想法如何节省计算时间(是的,没关系)内存费用?

3 个答案:

答案 0 :(得分:1)

我建议使用与输入矩阵完全相同的 2个辅助矩阵

现在让第一个为,第二个为。行矩阵填充为:

row[i][j] = sum of(a[i][0] + a[i][1] +....a[i][j-1])

你可以通过遍历行主要顺序遍历输入数组,轻松地及时做到 O(N * N)。列矩阵填充为:

column[i][j] = sum of(a[0][j] + a[1][j] +....a[i-1][j])

您也可以通过按列主要顺序遍历输入数组,轻松地及时 O(N * N)

现在通过遍历输入数组获得您的位置,总复杂度为:

时间复杂度= O(N * N)

空间复杂度= O(N * N)

答案 1 :(得分:1)

你在求和过程中所做的实际上是一个+形滤波器的卷积。如果你用一个调用scipy的convolve2d函数替换你的显式循环,你可以更快地得到这个,它将为你做所有必要的循环,但不是在python中,而是在C:

import numpy as np
from scipy.signal import convolve2d

# Your original array:
a = np.asarray([[1, 2, 3, 4],
                [5, 6, 7 ,8],
                [9, 10, 11, 12],
                [13, 14, 15, 16]])

# Building a filter that is essentially a + with arms of length 4
mask = np.zeros((2*4+1, 2*4+1))
mask[4, :] = 1
mask[:, 4] = 1

# Apply that filter to your array
sums = convolve2d(a, mask, mode="same")

# Find the maximum and its position in the sums array:
np.max(sums), np.unravel_index(np.argmax(sums), sums.shape)

最后,sums是给出原始数组中每个位置的求和过程值的数组。您只需找到最大值及其位置。

虽然复杂性可能不会比你的解决方案更好,但它仍然会快得多,因为python循环非常慢,而numpy和scipy中的很多机器都是用C / Fortran编写的,加快了计算速度

在100x100阵列上定时解决这个问题的解决方案可以提供大约的加速因子。在我的机器上40(78.7ms vs 2.06ms)。

答案 2 :(得分:1)

使用numpy slicing -

的解决方案
import numpy as np


def get_sum(matrix, row, col):
    # Get the sum of 4 numbers to the left
    sum_left_4_numbers = matrix[row, max(col - 4, 0):col].sum()
    # Get the sum of 4 numbers to the right
    sum_right_4_numbers = matrix[row, col:col + 4].sum()
    # Get the sum of 4 numbers above
    sum_top_4_numbers = matrix[max(row - 4, 0):row, col].sum()
    # Get the sum of 4 numbers below
    sum_bottom_4_numbers = matrix[row + 1: row + 4, col].sum()
    return sum_left_4_numbers + sum_right_4_numbers + sum_top_4_numbers + sum_bottom_4_numbers

matrix = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])
print get_sum(matrix, 1, 2)
# Output - 55