Efficient way to shift 2D-matrices in both directions?

前端 未结 4 1715
离开以前
离开以前 2021-02-07 15:29

Given a two dimensional matrix, e.g.

l = [[1,1,1],
     [2,5,2],
     [3,3,3]])

What is the most efficient way of implementing a shift operatio

相关标签:
4条回答
  • 2021-02-07 15:51

    This is a generic version you can rotate it in all four directions, any number of times

    l = [[1,1,1],
         [2,5,2],
         [3,3,3]]
    
    def shift(direction, count, myList):
        myLen = len(myList)
        if direction == "up":
            return [myList[i % myLen] for i in range(count, count + myLen)]
        elif direction == "down":
            return [myList[-i] for i in range(count, count - myLen, -1)]
        elif direction == "left":
            tlist = zip(*myList)
            return map(list, zip(*[tlist[i % myLen] for i in range(count, count + myLen)]))
        elif direction == "right":
            tlist = zip(*myList)
            return map(list, zip(*[tlist[-i] for i in range(count, count - myLen, -1)]))
    
    print shift("up", 1, l)
    print shift("up", 2, l)
    print shift("down", 2, l)
    print shift("down", 1, l)
    print shift("left", 1, l)
    print shift("right", 1, l)
    

    Output

    [[2, 5, 2], [3, 3, 3], [1, 1, 1]]
    [[3, 3, 3], [1, 1, 1], [2, 5, 2]]
    [[2, 5, 2], [3, 3, 3], [1, 1, 1]]
    [[3, 3, 3], [1, 1, 1], [2, 5, 2]]
    [[1, 1, 1], [5, 2, 2], [3, 3, 3]]
    [[1, 1, 1], [2, 2, 5], [3, 3, 3]]
    
    0 讨论(0)
  • Numpy provides a method called roll() to shift entries.

    >>> import numpy as np
    >>> x = np.arange(9)
    >>> x = x.reshape(3, 3)
    >>> print(x)
    
    [[0 1 2]
     [3 4 5]
     [6 7 8]]
    
    >>> x = np.roll(x, -1, axis=0) # up
    >>> print(x)
    
    [[3 4 5]
     [6 7 8]
     [0 1 2]]
    
    >>> x = np.roll(x, 1, axis=0) # down
    >>> print(x)
    
    [[0 1 2]
     [3 4 5]
     [6 7 8]]
    
    >>> x = np.roll(x, 2, axis=1) # right
    >>> print(x)
    
    [[1 2 0]
     [4 5 3]
     [7 8 6]]
    
    >>> x = np.roll(x, -2, axis=1) # left
    >>> print(x)    
    
    [[0 1 2]
     [3 4 5]
     [6 7 8]]
    

    I guess that Numpy will be pretty efficient compared to most solutions
    in terms of matrix operations and you won't be bound to a 2 dimensional matrix.

    0 讨论(0)
  • 2021-02-07 15:59

    Here's one fairly efficient way to do it that will work with non-square matrices:

    DIRS = NONE, UP, DOWN, LEFT, RIGHT = 'unshifted', 'up', 'down', 'left', 'right'
    
    def shift(matrix, direction, dist):
        """ Shift a 2D matrix in-place the given distance of rows or columns in the
            specified (NONE, UP, DOWN, LEFT, RIGHT) direction and return it.
        """
        if dist and direction in (UP, DOWN, LEFT, RIGHT):
            n = 0
            if direction in (UP, DOWN):
                n = (dist % len(matrix) if direction == UP else -(dist % len(matrix)))
            elif direction in (LEFT, RIGHT):
                n = (dist % len(matrix[0]) if direction == LEFT else -(dist % len(matrix[0])))
                matrix[:] = list(zip(*matrix))  # Transpose rows and columns for shifting.
    
            h = matrix[:n]
            del matrix[:n]
            matrix.extend(h)
    
            if direction in (LEFT, RIGHT):
                matrix[:] = map(list, zip(*matrix))  # Undo previous transposition.
    
        return matrix
    
    
    if __name__ == '__main__':
    
        # Some non-square test matrices.
        matrix1 = [[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9],
                   [10, 11, 12]]
    
        matrix2 = [[1, 2, 3, 4],
                   [5, 6, 7, 8],
                   [9, 10, 11, 12]]
    
        def shift_and_print(matrix, direction, dist):
            GAP =  2  # Plus one for a ":" character.
            indent = max(map(len, DIRS)) + GAP
            print(direction
                    + ': ' + (indent-2-len(direction))*' '
                    + ('\n'+indent*' ').join(map(str, shift(matrix, direction, dist)))
                    + '\n')
    
        for matrix in matrix1, matrix2:
            for direction in DIRS:
                shift_and_print(matrix, direction, 1)  # Printed results are cumulative.
    

    Output (note that the results are cumulative since the operations are performed in-place and the shifting is applied to the result of the previous call):

    no shift: [1, 2, 3]
              [4, 5, 6]
              [7, 8, 9]
              [10, 11, 12]
    
    up:       [4, 5, 6]
              [7, 8, 9]
              [10, 11, 12]
              [1, 2, 3]
    
    down:     [1, 2, 3]
              [4, 5, 6]
              [7, 8, 9]
              [10, 11, 12]
    
    left:     [2, 3, 1]
              [5, 6, 4]
              [8, 9, 7]
              [11, 12, 10]
    
    right:    [1, 2, 3]
              [4, 5, 6]
              [7, 8, 9]
              [10, 11, 12]
    
    no shift: [1, 2, 3, 4]
              [5, 6, 7, 8]
              [9, 10, 11, 12]
    
    up:       [5, 6, 7, 8]
              [9, 10, 11, 12]
              [1, 2, 3, 4]
    
    down:     [1, 2, 3, 4]
              [5, 6, 7, 8]
              [9, 10, 11, 12]
    
    left:     [2, 3, 4, 1]
              [6, 7, 8, 5]
              [10, 11, 12, 9]
    
    right:    [1, 2, 3, 4]
              [5, 6, 7, 8]
              [9, 10, 11, 12]
    
    0 讨论(0)
  • 2021-02-07 16:01

    Maybe something like this using numpy:

    def shift(x, direction='up'):
        if direction == 'up':
            temp = range(x.shape[0])
            indicies = temp[1:] + [temp[0]]
            return x[indicies]
        elif direction == 'left':
            temp = range(x.shape[1])
            indicies = temp[1:] + [temp[0]]
            return x[:, indicies]
        else:
            print 'Error direction not known'
    

    Result:

    >>> shift(l, direction='up')
    array([[2, 5, 2],
           [3, 3, 3],
           [1, 1, 1]])
    >>> shift(l, direction='left')
    array([[1, 1, 1],
           [5, 2, 2],
           [3, 3, 3]])
    >>> shift(l, direction='to the moon')
    Error direction not known
    
    0 讨论(0)
提交回复
热议问题