有没有更有效的方法来解决这个问题?

时间:2015-06-11 20:34:29

标签: python python-2.7 optimization matrix

考虑以下N×N矩阵 A B 只包含正整数:

  • B 是通过对奇数进行求和而从 A 创建的 邻居每个元素,位于 A 中的[i,j]位置。
  • 然后将每个计算的总和存储在 B 中的相应位置[i,j]

例如:

  • [3, 2]位置,有4个奇数[3, 1, 5, 3] 邻居A[3,2]
  • 这些相邻奇数的总和为12, 存储在B[3,2]

此代码通过蛮力工作,分别对9个区域中的每个区域进行操作(即:顶部,底部,左侧,右侧,4个角和内部)。

有没有更好的方法来解决这个问题?

from numpy import array,zeros,shape

def sum_neighbours(A):
    A = array(A,int)
    r,c = shape(A)
    B = zeros(shape(A),int)

    for i in range(r):
        for j in range(c):
            N_list_i = [0,-1,-1,-1,0,1,1,1,0] #[l,tl,t,tr,r,br,b,bl]
            N_list_j = [-1,-1,0,1,1,1,0,-1,-1]          

            # innerSquares
            if 1 <= i <= (r-2) and 1 <= j <= (r-2):
                for k in range(len(N_list_j)-1):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #TopLeft            
            elif i==0 and j==0:
                N_list_i = N_list_i[4:(4+3)]
                N_list_j = N_list_j[4:(4+3)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #BottomLeft            
            elif j==0 and i==(r-1):
                N_list_i = N_list_i[2:(2+3)]
                N_list_j = N_list_j[2:(2+3)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #TopRight            
            elif i==0 and j == (r-1):
                N_list_i = N_list_i[6:(6+3)]
                N_list_j = N_list_j[6:(6+3)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #BottomRight            
            elif i == (r-1) and j == (r-1):
                N_list_i = N_list_i[0:(0+3)]
                N_list_j = N_list_j[0:(0+3)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #TopBorder            
            elif i==0 and j < (r-1):
                N_list_i = N_list_i[4:(4+5)]
                N_list_j = N_list_j[4:(4+5)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #LeftBorder            
            elif j==0 and i < (r-1):
                N_list_i = N_list_i[2:(2+5)]
                N_list_j = N_list_j[2:(2+5)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #BottomBorder            
            elif i==(r-1) and j < (r-1):
                N_list_i = N_list_i[0:(0+5)]
                N_list_j = N_list_j[0:(0+5)]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]

            #RightBorder            
            elif j==(r-1) and i < (r-1):
                N_list_i = [1,1,0,-1,-1]
                N_list_j = [0,-1,-1,-1,0]
                for k in range(len(N_list_j)):
                    if A[i+N_list_i[k]][j+N_list_j[k]] %2 != 0:
                        B[i][j] += A[i+N_list_i[k]][j+N_list_j[k]]
    return B

A = array([[3,2,8,1,1],
           [1,2,4,1,1],
           [7,3,4,1,8],
           [1,8,0,6,4],
           [5,6,5,3,8]])

4 个答案:

答案 0 :(得分:1)

在重构方面,你几乎肯定想要避免&#34;蛮力&#34;在必须明确键入所有案例的意义上:) @alexmcf解决了上述问题。

在概念上,您的方法直接遵循问题陈述:检查矩阵中每个数字的所有邻居并将所有奇数相加。这意味着您总是在进行检查和求和,即使矩阵中没有奇数。

作为替代方案:我们可以首先浏览矩阵并识别奇数。然后,从空矩阵开始,我们只需将奇数加到所有有效邻居。这节省了与矩阵中奇数的稀缺成比例的工作。

import numpy as np

def find_offsets(row, col, size):
    """Return all valid pairs of offsets as [(row_offset, col_offset)...]."""
    offsets = ((-1, -1), (-1, 0), (-1, 1),
               (0, -1), (0, 1),
               (1, -1), (1, 0), (1, 1))

    return [(r_off, c_off) for r_off, c_off in offsets
            if row + r_off >= 0 and row + r_off < size
            if col + c_off >= 0 and col + c_off < size]

def find_odds(matrix, size):
    """Return all odd values in matrix as [(row_ind, col_ind, value)...]."""
    return [(row, col, matrix[row][col])
            for row in xrange(size)
            for col in xrange(size)
            if matrix[row][col] % 2 != 0]

def gen_matrix(source, size):
    """Filter source 2x2 matrix for odds and add each to valid neighbors."""

    out = np.zeros((size, size), dtype=int)

    # filter for location and value of all odd numbers
    odds = find_odds(source, size)
    for row, col, value in odds:
        # add odd number to all valid neighbors (by finding valid offsets)
        offsets = find_offsets(row, col, size)
        for r_off, c_off in offsets:
            out[row + r_off][col + c_off] += value

    return out

def sum_neighbors():
    """Sum neighbors as described in problem."""
    N = 5
    A = [[3,2,8,1,1],
         [1,2,4,1,1],
         [7,3,4,1,8],
         [1,8,0,6,4],
         [5,6,5,3,8]]
    return gen_matrix(A, N)

以上运行速度大约是代码的两倍(对于苹果与苹果的比较,我在iPython中执行时调整了上述内容,以便在两种情况下都提供相同的矩阵A作为参数):

In [19]: %timeit original.sum_neighbours(A)
1000 loops, best of 3: 195 µs per loop

In [20]: %timeit new.sum_neighbors(A)
10000 loops, best of 3: 84 µs per loop

答案 1 :(得分:0)

工作代码

import numpy as np

def sum_neighbours_NEW(A):
    A = np.array(A,int)
    r,c = np.shape(A)
    B = np.zeros(A.shape, int)

    def b(x,y, size=A.shape):
        xlim,ylim = size # get limits of axes

        # generate a list of all i,j bordering x,y
        # this will cause the most slowdown... for faster code, you minimise   
        # the number of tims idx gets called by making it a parameter of b()
        # adding x and y and doing a np.where() to ensure it doesn't 
        # index out of the array range
        idx = [[x+i,y+j] for i in range(-1,2) for j in range(-1,2) \
               if (i,j) != (0,0) and (x+i>=0 and y+j>=0) and (x+i<xlim and y+j<ylim)]
        idx = np.asarray(idx) # convert to numpy array
        return idx[:,0], idx[:,1] # split into x,y for indexing

    # iterate across all elements of A
    for i in xrange(r):
        for j in xrange(c):
            els = A[b(i,j)]
            sum_A = els[np.where(els % 2)].sum() # only sum odd elements
            B[i,j] = sum_A
    return B

测试

A = array([[3,2,8,1,1],
           [1,2,4,1,1],
           [7,3,4,1,8],
           [1,8,0,6,4],
           [5,6,5,3,8]])

# check new function vs. old function
(sum_neighbours(A) == sum_neighbours_NEW(A)).all()
True

解释

尝试使用此循环扫描“周围元素”,它会返回x,y元素的元组,这些元素分为numpy indexing <指定的单个xy数组/ p>

def b(x,y, size=A.shape):
    xlim,ylim = size # get limits of axes

    # generate a list of all i,j bordering x,y
    idx = [[x+i,y+j] for i in range(-1,2) for j in range(-1,2) \
           if (i,j) != (0,0) and (x+i>=0 and y+j>=0) and (x+i<xlim and y+j<ylim)]
    idx = np.asarray(idx) # convert to numpy array
    return idx[:,0], idx[:,1] # split into x,y for indexing

idx = b(3,2) 
# check correct by matching indexes e.g. 
# (idx[0][0],idx[1][0]) => (2,1) , (idx[0][1],idx[1][1]) => (2,2) etc.
print idx 
(array([2, 2, 2, 3, 3, 4, 4, 4]), array([1, 2, 3, 1, 3, 1, 2, 3]))

然后您可以通过以下方式访问 A 中的元素

els = A[idx]
print els
array([3, 4, 1, 8, 6, 6, 5, 3])

然后你可以通过以下方式对所有奇数元素求和:

print els[np.where(els % 2)].sum()
12

使用此方法获取 A 中每个元素的总和。

答案 2 :(得分:0)

这部分应该有助于获得奇怪的元素。

import numpy as np
# generate an array of random integers
A = np.random.random_integers(1,10, [20,10])
# get only the odd values back from the array
A_odds = A*(A%2!=0)
# pad the array with zeros to make summing easier
A_odds_padded = np.pad(array=A_odds, pad_width=1, mode='constant')
# iterate elements and sum
B = np.zeros(np.shape(A))
for i in range(shape(B)[0]):
    for j in range(shape(B)[1]):
        #etc

答案 3 :(得分:-2)

我有这个:

def find_odd_sum(arr):
    # Pad the original array
    arr_pad = np.pad(arr, 1, 'constant', constant_values=0)

    shape = np.shape(arr)
    arr_b = np.zeros((shape[0] + 2, shape[1] + 2))

    for ii in range(0, shape[0]):  #rows
        for jj in range(0, shape[1]):  #cols
            point = np.array([ii+1, jj+1])
            # Points to check
            kernel = [np.array([-1, 0]),
                      np.array([0, 1]), 
                      np.array([1, 0]), 
                      np.array([0, -1]),
                      np.array([-1, -1]),
                      np.array([1, 1]),
                      np.array([-1, 1]),
                      np.array([1, -1])]
            points = [k+point for k in kernel]
            s = [arr_pad[points[i][0], points[i][1]] for i in range(len(points))]
            s1 =sum([i for i in s if i % 2 != 0])
            arr_b[point[0], point[1]] = s1

    return arr_b[1:-1, 1:-1]



>>>
[[  1.   4.   2.   3.   3.]
 [ 13.  14.   6.   4.   4.]
 [  5.   9.   5.   2.   3.]
 [ 15.  21.  12.   9.   4.]
 [  1.  11.   3.   5.   3.]]