Numpy:如何在矩阵A中找到子矩阵的唯一局部最小值?

时间:2016-11-17 11:39:18

标签: python algorithm numpy matrix

给定维数为MxN(4x4)的矩阵A,如何找到每个2x2子矩阵的下一个最佳最小值?

A = array([[ 32673.    ,  15108.2   ,  26767.2   ,   9420.   ],
           [ 32944.2   ,  14604.01  ,  26757.01  ,   9127.2  ],
           [ 26551.2   ,  9257.01   ,  26595.01  ,   9309.2  ],
           [ 26624.    ,   8935.2   ,  26673.2   ,   8982.   ]])

一组子矩阵的下一个最佳最小值是该子矩阵中与其他最小值的本地位置不冲突的最小值:

示例算法:

1. Find the minimum in A: 8935.2 global coords[3,1], local coords [1,1]
2. No other matrix has been evaluated so no conflict yet.
3. Find the next submatrix min: 8982. gc [3,3], lc [1,1]
4. Conflict exists, find next min in same submatrix: 9309.2 gc [2,3], lc [0,1]
5. Find next submatrix min: 9420 gc [0,3] lc[0,1]
6. Conflict exists, find next min: 26757.01 gc [1,2] lc [1,0]
7. Find next submatrix min: 14604 -- conflict with lc[1,1]
8. Find next submatrix min: 15108.2 -- conflict with lc [0,1]
9. Find next submatrix min: 32673. gc [0,0], lc [0,0]
我尝试过的一种方法是遵循上面的算法,但不是再次详尽地搜索每个子矩阵,而是全局更新每个子矩阵本地位置,并使用“高”字符。值(>> max(A)),在每次成功找到最小值时递增。

预期输出将是一个列表:

[((0, 0), (0, 0), 32673), ((0, 1), (1, 0), 26757.01), ((1, 0), (1, 1), 8935.2), ((1, 1), (0, 1), 9309.2)]
形式为[((t1),(t2),value)...]的

,其中t1是A中子矩阵的坐标,t2是子矩阵中所选最小值的坐标。

编辑:子矩阵定义为ZxZ,其中MxN模数为ZxZ == 0,并且从(0,0)开始非重叠,并平铺以匹配MxN的维数。< / p>

编辑:以下是我构建的解决方案,但速度很慢。我怀疑如果我在每次迭代时从矩阵中删除子矩阵,那么性能可能会提高,但我不知道该怎么做。

    def get_mins(self, result):
    # result is the 2d array
    dim = 2  # 2x2 submatrix
    mins = []
    count = 0
    while count < dim**2:
        a, b = result.shape
        M4D = result.reshape(a//dim, dim, b//dim, dim)
        lidx = M4D.transpose(0, 2, 1, 3).reshape(-1, b//dim, dim**2).argmin(-1)
        r, c = numpy.unravel_index(lidx, [dim, dim])

        yy = M4D.min(axis=(1, 3))
        ww = numpy.dstack((r, c))

        super_min = numpy.unravel_index(numpy.argmin(yy), (dim, dim))

        rows = super_min[0]
        cols = super_min[1]

        # ww[rows,cols] g_ves us 2x2 position
        offset_r, offset_c = ww[rows, cols]
        # super_min gives us submatrix position

        mins.append((tuple(super_min), (offset_r, offset_c), yy.min()))

        if dim > 1:
            # update all other positions with inf >> max(result)
            result[numpy.ix_([offset_r + (d * dim) for d in range(dim)], [offset_c + (d * dim) for d in range(dim)])] = numpy.inf
            # update the submatrix to all == numpy.inf
            result[rows*dim:((rows*dim)+dim), cols*dim:((cols*dim)+dim)] = numpy.inf
        count += 1
    return mins

3 个答案:

答案 0 :(得分:2)

你仍然不太清楚子矩阵定义,但是根据你的预期输出,我推断你想把它分成4个非重叠数组 - 我可以用重塑和转置创建:

In [113]: A1=A.reshape(4,2,2).transpose(0,2,1)
In [114]: A1
Out[114]: 
array([[[ 32673.  ,  26767.2 ],
        [ 15108.2 ,   9420.  ]],

       [[ 32944.2 ,  26757.01],
        [ 14604.01,   9127.2 ]],

       [[ 26551.2 ,  26595.01],
        [  9257.01,   9309.2 ]],

       [[ 26624.  ,  26673.2 ],
        [  8935.2 ,   8982.  ]]])

argmin给出每个位置(在扁平的coor中)

In [115]: np.argmin(A1[1])
Out[115]: 3
In [116]: [np.argmin(a) for a in A1]
Out[116]: [3, 3, 2, 2]

因此使用2x2子阵列没有真正的优势 - 让我们把它们拉出来并坚持使用更简单的1d - 和单个argmin值

In [117]: A2=A1.reshape(4,4)
In [118]: A2
Out[118]: 
array([[ 32673.  ,  26767.2 ,  15108.2 ,   9420.  ],
       [ 32944.2 ,  26757.01,  14604.01,   9127.2 ],
       [ 26551.2 ,  26595.01,   9257.01,   9309.2 ],
       [ 26624.  ,  26673.2 ,   8935.2 ,   8982.  ]])
In [119]: [np.argmin(a) for a in A2]
Out[119]: [3, 3, 2, 2]

最后,我可以将这些索引转换回2d:

In [123]: [np.unravel_index(np.argmin(a),(2,2)) for a in A2]
Out[123]: [(1, 1), (1, 1), (1, 0), (1, 0)]

我认为其余的只是对这个A2结构的迭代搜索。

In [124]: A2[1:,3]=np.inf
In [125]: [np.argmin(a) for a in A2]
Out[125]: [3, 2, 2, 2]
In [126]: A2[2:,2]=np.inf
In [127]: [np.argmin(a) for a in A2]
Out[127]: [3, 2, 0, 0]
In [128]: A2[3:,0]=np.inf
In [129]: [np.argmin(a) for a in A2]
Out[129]: [3, 2, 0, 1]

In [139]: A2
Out[139]: 
array([[ 32673.  ,  26767.2 ,  15108.2 ,   9420.  ],
       [ 32944.2 ,  26757.01,  14604.01,       inf],
       [ 26551.2 ,  26595.01,       inf,       inf],
       [      inf,  26673.2 ,       inf,       inf]])
哎呀,我以为我已经弄清楚你是如何定义子矩阵的,但看起来并不正确。但我会留下这个答案。它可以帮助您解决问题。

答案 1 :(得分:2)

嗯,比预期更多的工作^^。 该算法大致是:

  • 在1d数组aSrt
  • 中对矩阵a的值进行排序
  • 循环通过aSrt并根据此值
  • 识别2x2子矩阵
  • 如果我们在lstSubMat中没有此子矩阵,则按子矩阵(0,0)的全局坐标添加
  • 现在我们有一个列表lstSubMat,其中包含按最小值排序的子矩阵
  • 对于每个子矩阵,我们现在在本地坐标仍然可用的前提条件下找到最小值(即不在msk中监听)。它存储在结果中(通过全局坐标)

您可以在下面的代码中看到的是:

  • 如何在ndarrays中找到元素
  • 如何按第二列索引逐行排序adarray,然后按第一列索引
  • 排序
  • 如何转换tupels中的列表,反之亦然

代码:

#lc: local coordinates
#gc: global coordinates
#sc: submatrix coordinates


import numpy as np
a = np.array(
    [[ 32673.    ,  15108.2   ,  26767.2   ,   9420.   ],
    [ 32944.2   ,  14604.01  ,  26757.01  ,   9127.2  ],
    [ 26551.2   ,  9257.01   ,  26595.01  ,   9309.2  ],
    [ 26624.    ,   8935.2   ,  26673.2   ,   8982.   ]]
    )
#print(a)



#sort values of a in 1d array
aSrt=np.sort(a.flatten())
#print(aSrt)

#list of submatrix coordinates ordered by their minimum
lstSubMat=[]
for ii in range(0,len(aSrt)):
    #print('just to make things clear:',np.where(a==aSrt[ii]))
    gc=[elem[0] for elem in list(np.where(a==aSrt[ii]))]
    lc = [elem%2 for elem in gc]
    sc = [gc[jj]-lc[jj] for jj in range(0,2)]
    #print('gc:',gc,'sc',sc,'lc:',lc, 'value:',aSrt[0])
    if not sc in lstSubMat:
        lstSubMat.append(sc)
        #lstSubMat[1].append(lc)
        #lstSubMat[2].append(value)

# result is list of gc
result=np.empty((4,2),dtype=int)
#result=np.empty([4,2])
nmbFound=0

#check list with lc
msk=[]

while nmbFound<4:
    sc=lstSubMat[0]
    subMat=a[sc[0]:sc[0]+2,sc[1]:sc[1]+2]
    #print('subMat:',subMat)
    valSubMatSrt=np.sort(subMat.flatten())
    for ii in range(0,4):
        lc=[elem[0] for elem in list(np.where(subMat==valSubMatSrt[ii]))]
        if not lc in msk:
            msk.append(lc)
            #result.append([sc[jj]+lc[jj] for jj in range(0,2)])
            #result[nmbFound]=[sc[jj]+lc[jj] for jj in range(0,2)]
            result[nmbFound,0]=sc[0]+lc[0]
            result[nmbFound,1]=sc[1]+lc[1]
            nmbFound+=1
            #print('gc:',result[-1],'sc',sc,'lc:',lc, 'value:',aSrt[0])
            lstSubMat=lstSubMat[1:]
            break

#print(result)

#sort first by row then by col index of submatrix -> //2
result=result[(result[:,1]//2).argsort()] 
result=result[(result[:,0]//2).argsort()] 
#print(result)

print('\n\nresult:')
for ii in range(0,len(result)):
    sc=tuple([elem//2 for elem in result[ii,:]])
    lc=tuple([result[ii,jj]%2 for jj in range(0,2)])
    print(sc,lc,a[tuple(result[ii,:])])

输出:

result:
(0, 0) (0, 0) 32673.0
(0, 1) (1, 0) 26757.01
(1, 0) (1, 1) 8935.2
(1, 1) (0, 1) 9309.2

答案 2 :(得分:2)

考虑到迭代选择全局最小值之间的依赖关系,这里采用单循环方法 -

def unq_localmin(A, dim):
    m, n = A.shape
    M4D = A.reshape(m//dim, dim, n//dim, dim)
    M2Dr = M4D.swapaxes(1,2).reshape(-1,dim**2)
    a = M2Dr.copy()

    N = M2Dr.shape[0]
    R = np.empty(N,dtype=int)
    C = np.empty(N,dtype=int)
    shp = M2Dr.shape
    for i in range(N):
        r,c = np.unravel_index(np.argmin(a),shp)
        a[r] = np.inf
        a[:,c] = np.inf
        R[i], C[i] = r, c
    out = M2Dr[R,C]
    idr = np.column_stack(np.unravel_index(R,(dim,dim)))
    idc = np.column_stack(np.unravel_index(C,(dim,dim)))
    return zip(map(tuple,idr),map(tuple,idc),out)

让我们使用随机较大的9x9数组和3x3子矩阵/子阵列验证结果,以便根据OP的实施情况测试多样性{ - 1}} -

get_mins

<强>简化

上面的解决方案为我们提供了行和列索引,这些索引可用于构造用In [66]: A # Input data array Out[66]: array([[ 927., 852., 18., 949., 933., 558., 519., 118., 82.], [ 939., 782., 178., 987., 534., 981., 879., 895., 407.], [ 968., 187., 539., 986., 506., 499., 529., 978., 567.], [ 767., 272., 881., 858., 621., 301., 675., 151., 670.], [ 874., 221., 72., 210., 273., 823., 784., 289., 425.], [ 621., 510., 303., 935., 88., 970., 278., 125., 669.], [ 702., 722., 620., 51., 845., 414., 154., 154., 635.], [ 600., 928., 540., 462., 772., 487., 196., 499., 208.], [ 654., 335., 258., 297., 649., 712., 292., 767., 819.]]) In [67]: unq_localmin(A, dim = 3) # Using proposed approach Out[67]: [((0, 0), (0, 2), 18.0), ((2, 1), (0, 0), 51.0), ((1, 0), (1, 2), 72.0), ((1, 1), (2, 1), 88.0), ((0, 2), (0, 1), 118.0), ((2, 2), (1, 0), 196.0), ((2, 0), (2, 2), 258.0), ((1, 2), (2, 0), 278.0), ((0, 1), (1, 1), 534.0)] In [68]: out = np.empty((9,9)) In [69]: get_mins(out,A) # Using OP's soln with dim = 3 edited Out[69]: [((0, 0), (0, 2), 18.0), ((2, 1), (0, 0), 51.0), ((1, 0), (1, 2), 72.0), ((1, 1), (2, 1), 88.0), ((0, 2), (0, 1), 118.0), ((2, 2), (1, 0), 196.0), ((2, 0), (2, 2), 258.0), ((1, 2), (2, 0), 278.0), ((0, 1), (1, 1), 534.0)] 打印的索引元组。如果您不需要这些,我们可以稍微简化一下提议的方法,如此 -

get_mins

运行时测试 -

def unq_localmin_v2(A, dim):
    m, n = A.shape
    M4D = A.reshape(m//dim, dim, n//dim, dim)
    M2Dr = M4D.swapaxes(1,2).reshape(-1,dim**2)    
    N = M2Dr.shape[0]
    out = np.empty(N)
    shp = M2Dr.shape
    for i in range(N):
        r,c = np.unravel_index(np.argmin(M2Dr),shp)
        out[i] = M2Dr[r,c]
        M2Dr[r] = np.inf
        M2Dr[:,c] = np.inf        
    return out