简体   繁体   English

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

[英]Is there a more efficient way to solve this?

Consider the following N × N matrices A and B that contain only positive integers: 考虑以下仅包含正整数的N×N个矩阵AB

  • B is created from A by summing the odd numbers that neighbour each element, at position [i,j] in A . B是通过在A中位置[i,j]求和与每个元素相邻的奇数而从A中创建
  • Each computed sum is then stored in the corresponding position [i,j] in B . 然后将每个计算出的总和存储在B中的对应位置[i,j]中。

For example: 例如:

  • At position [3, 2] there are 4 odd numbers [3, 1, 5, 3] that neighbour A[3,2] . 在位置[3, 2]4奇数[3, 1, 5, 3]该邻居A[3,2]
  • The sum of these neighbouring odd numbers is 12 , which is stored in B[3,2] 这些相邻的奇数之和为12 ,存储在B[3,2]

This code works by brute force, taking each of the 9 regions separately, (Namely: Top, bottom, left, right, 4 corners and inner). 该代码通过蛮力工作,将9个区域分别分开(即:上,下,左,右,4个角和内部)。

Is there a better way to go about this? 有没有更好的方法来解决这个问题?

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]])

In terms of refactoring, you almost certainly want to avoid "brute force" in the sense of having to explicitly type out all your cases :) @alexmcf addresses this above. 在重构方面,您几乎肯定希望避免“蛮力”,因为必须明确地键入所有情况:) @alexmcf解决了上述问题。

In conceptual terms, your approach follows the problem statement directly: check all neighbors for each number in the matrix and sum all odd numbers. 从概念上讲,您的方法直接遵循问题陈述:检查矩阵中每个数字的所有邻居并将所有奇数求和。 This means that you are always doing the checking and summing, even if there are no odd numbers in the matrix. 这意味着即使矩阵中没有奇数始终要进行检查和求和

As an alternative: we can first run through the matrix and identify odd numbers. 作为替代方案:我们可以首先遍历矩阵并确定奇数。 Then, starting from a null matrix, we simply add the odd numbers to all valid neighbors. 然后,从一个零矩阵开始,我们只需将奇数添加到所有有效邻居中即可。 This saves work in proportion to the sparcity of odd numbers in the matrix. 与矩阵中奇数稀疏性成比例地节省了工作。

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)

The above runs about twice as fast as your code (for an apples-to-apples comparison, I tweaked the above when executing in iPython so that the same matrix A is provided as a parameter in both cases): 上面的代码运行速度是您代码的两倍(为了进行苹果之间的比较,我在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

Working Code 工作守则

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

Test 测试

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

Explanation 说明

Try this loop for scanning across the 'surrounding elements', it returns a tuple of x,y elements split into individual x and y arrays as specified by numpy indexing 尝试执行此循环以扫描“周围的元素”,它返回x,y元素的元组x,y元素分为numpy索引指定的单个xy数组

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]))

Then you can access elements in A by: 然后,您可以通过以下方式访问A中的元素:

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

You can then sum all elements that are odd by: 然后可以通过以下方式求和所有奇数元素:

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

use this to obtain the sum across every element in A . 用它来获得A中每个元素的和。

The top portion of this should help out in getting just the odd elements. 顶部应该有助于获得奇怪的元素。

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

I have this: 我有这个:

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.]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM