【问题标题】:Summing elements in a multi-dimensional array within a certain area对某个区域内的多维数组中的元素求和
【发布时间】:2018-09-07 22:30:12
【问题描述】:

我正在创建一个机器人穿越二维数组地图的游戏。二维数组中的每个点都有一个“宝藏”,即许多硬币。我希望能够从机器人当前位置向上、向下、向右和向左添加最多 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] 或坐标 11,当我移动到 a[2][3] 或坐标 12 时,差异在于:

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

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

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

有什么想法可以节省计算时间(是的,没关系)以内存为代价?

【问题讨论】:

  • 如何将字段设为 numpy 数组并使用切片及其 sum 函数?
  • @jotasi 似乎是一个很好的答案,需要澄清:)
  • 如果我猜对了,那么 a[1][2](第 7 点),它会是 7+3+6+5+8+11+15 但你说的是 a[1][ 2](第 7 点),它会返回 (7+1)+(8)+(5+6)+(11+15) ?
  • @AyodhyankitPaul 不错!你完全正确
  • @JohnLexus 数组大小将相同,或者它可以大小(任意大小)?

标签: python algorithm multidimensional-array


【解决方案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 阵列上的解决方案相比较,可以提供大约 100 倍的加速因子。在我的机器上为 40(78.7 毫秒对 2.06 毫秒)。

【讨论】:

  • 非常喜欢这个解决方案!
  • 我接受这个答案是因为它更快更干净,尽管我认为 Sumeet 的答案在时间复杂度方面是“更好”的答案
  • @JohnLexus Btw 对于一个比数组大小小得多的掩码,据我所知,所有三种解决方案(你的、Sumeet 的和我的)都具有 N*N 的复杂性。
  • 哦,真的吗?我以为我的会超过 n^2。但是现在我想起来了,你是对的,这是 n^2
  • 我猜这不是那么简单,因为至少在你的例子中,步数是数组大小的顺序。在这种情况下,复杂性会更糟。但是一旦数组变得比步数大得多,你就会留下两个循环和一些需要恒定(步数依赖)时间的东西。还是我在这里忽略了什么?
【解决方案2】:

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

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

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)

【讨论】:

  • 就时间复杂性而言,这是更好的答案,基本上正是我所寻找的,就像一个魅力。希望我也能接受这个答案!
  • @JohnLexus 您可以接受任何符合您目的的答案。
【解决方案3】:

使用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

【讨论】:

  • 他希望找到最大的总和,因此您的解决方案需要遍历所有元素以找到最大值。
  • 是的。由于 numpy,它比使用 python 列表更快。不过,这并不是一个显着的改进。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-27
相关资源
最近更新 更多