【问题标题】:How can I generate matrix combination based on provided matrices of 0 and 1 in python?如何根据python中提供的0和1矩阵生成矩阵组合?
【发布时间】:2021-01-05 08:37:55
【问题描述】:

我有一个 3x3 的零矩阵和一个 2x2 的矩阵:

a = np.zeros((3, 3), dtype=int)
b = np.ones((2, 2), dtype=int)
array([[0, 0, 0],
       [0, 0, 0],
       [0, 0, 0]])
array([[1, 1],
       [1, 1]])

我想从ab 生成所有可能的矩阵组合,如下所示:

array([[1, 1, 0],
       [1, 1, 0],
       [0, 0, 0]])

array([[0, 1, 1],
       [0, 1, 1],
       [0, 0, 0]])

array([[0, 0, 0],
       [1, 1, 0],
       [1, 1, 0]])

array([[0, 0, 0],
       [0, 1, 1],
       [0, 1, 1]])

有什么方法可以快速做到这一点吗?我尝试使用for 循环来修改矩阵a 的值,但比较麻烦。任何帮助将不胜感激。

【问题讨论】:

  • 我猜你想将其概括为 a = mxm 和 b = nxn,其中 m>n。是吗?
  • @MohammedAadil 是的。我只提供上面的 a 和 b 矩阵以使其更直观。
  • 它们总是方阵吗?
  • @MohammedAadil No. a 和 b 也可以是矩形。仅供参考,我实际上是在尝试了解 RPG 中的库存分类机制。 a 矩阵应该是库存,b 矩阵是项目。
  • 这很容易解决。我在下面添加了新答案。

标签: python arrays numpy matrix


【解决方案1】:

您可以尝试以下方法:

m1, m2 = 4, 4
n1, n2 = 2, 3

a=np.zeros((m1,m2),dtype=int)
b=np.ones((n1,n2),dtype=int)

d1 = m1 - n1 + 1
d2 = m2 - n2 + 1

for i in range(d1):
    for j in range(d2):
        temp = a.copy()
        temp[i:i+n1,j:j+n2] = b
        print(temp)

输出:

[[1 1 1 0]
 [1 1 1 0]
 [0 0 0 0]
 [0 0 0 0]]
[[0 1 1 1]
 [0 1 1 1]
 [0 0 0 0]
 [0 0 0 0]]
[[0 0 0 0]
 [1 1 1 0]
 [1 1 1 0]
 [0 0 0 0]]
[[0 0 0 0]
 [0 1 1 1]
 [0 1 1 1]
 [0 0 0 0]]
[[0 0 0 0]
 [0 0 0 0]
 [1 1 1 0]
 [1 1 1 0]]
[[0 0 0 0]
 [0 0 0 0]
 [0 1 1 1]
 [0 1 1 1]]

适用于所有形状的矩阵。

【讨论】:

  • 非常感谢。这正是我正在寻找的。​​span>
  • 有更好的方法来做到这一点,即使是循环
【解决方案2】:

假设您有一个 (A, B) 零数组 (a) 和 (M, N) 一数组 (b)。按轴拆分问题。在第一个轴上可以占据A - M + 1 的位置,在第二个轴上可以占据B - N + 1,对于任何其他轴,依此类推。因此,可能性总数为(A - M + 1) * (B - N + 1)

可视化不同位置的最直观的方法是考虑将b 放入a 并带有一些偏移量。但还有另一种方式。我们可以将任何给定的位置看作是一个数组的视图,该数组被放置在一个四面都填充了零的数组中:

m = np.pad(b, np.subtract(a.shape, b.shape))

现在您可以为您感兴趣的每个位置创建m 的视图。例如,如果我们使用A = B = 3M = N = 2,第一个位置是m[1:4, 1:4],第二个位置是m[1:4, 0:3]m 看起来像这样:

array([[0, 0, 0, 0],
       [0, 1, 1, 0],
       [0, 1, 1, 0],
       [0, 0, 0, 0]])

如果您将 m 创建为 C 连续数组,则可以使用 np.lib.stride_tricks.as_strided 的神秘魔力一次获取所有可能的视图。从m[A - M, B - N] 开始,您可以添加两个额外的维度,只需后退一个元素,在前两个维度中使用与m 相同的步幅。这很容易推广到两个以上的维度:

def get_positions(a_shape, b_shape):
    # Check input shapes
    if len(a_shape) != len(b_shape):
        raise ValueError('a and b must have the same number of dimensions')
    d = np.subtract(a_shape, b_shape)
    if (d < 0).any():
        raise ValueError('a must be larger than b')

    # Make padded buffer
    m = np.pad(np.ones(b_shape, np.uint8), d)
    # Find initial offset as a tuple of slices
    offset = tuple(slice(off, None) for off in d)
    # Find new dimensions
    shape = tuple(d + 1) + a_shape
    # Find new strides
    strides = tuple(-s for s in m.strides) + m.strides
    # Make a view
    view = np.lib.stride_tricks.as_strided(m[offset], shape=shape, strides=strides)
    # Return a copy with the leading dims merged
    return view.reshape(-1, *a_shape)

最后的reshape 操作被强制复制数组,因为否则它是m 缓冲区中非常不连续的视图。如果您同意,只需返回两倍维度的视图即可。

你原来的例子变成:

>>> get_positions((3, 3), (2, 2))
array([[[1, 1, 0],
        [1, 1, 0],
        [0, 0, 0]],

       [[0, 1, 1],
        [0, 1, 1],
        [0, 0, 0]],

       [[0, 0, 0],
        [1, 1, 0],
        [1, 1, 0]],

       [[0, 0, 0],
        [0, 1, 1],
        [0, 1, 1]]], dtype=uint8)

这种矢量化解决方案适用于任意数量的维度。您还可以在不复制数据的 2D 中进行简化的 for 循环:

a = np.zeros((A, B), dtype=np.uint8)
b = np.ones((M, N), dtype=np.uint8)
m = np.pad(b, (A - M, B - N))

for i in range(A - M, -1, -1):
    for j in range(B - N, -1, -1):
        print(m[i:i + A, j:j + B])

您可以使用itertools.product 轻松推广到任意维度:

from itertools import product

a_shape = (...)
b_shape = (...)
d = np.subtract(a_shape, b_shape)
m = np.pad(np.ones(b_shape, dtype=np.uint8), d)

for offset in product(*[range(off, -1, -1) for off in d]):
    index = tuple(slice(off, off + sz) for off, sz in zip(offset, a_shape))
    print(m[index])

get_positions 函数上使用循环的优势可能有三方面:

  1. 我希望循环更快
  2. 循环使用较少的内存,因为它提供真实视图而不是复制整个数组。
  3. 循环允许您使用 numpy 支持的全部 32 个维度,而该函数将一半用于辅助轴。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多