domingo, 17 de novembro de 2013

Altas rotações

Um dos problemas do livro (problema 6.8) é pedido um programa que permita rodar de 90 graus uma matriz representada por uma lista de listas: uma lista com as linhas sendo que estas são também elas listas com os elementos na respectiva coluna. Um aluno de IPRP resolveu, e bem, ser mais ambicioso e implementar um programa que permita rodar 90, 128 ou 270 graus. Enviou-me o programa que reproduzo.
import copy
def rodar(matriz, grau):
    
    nova_matriz = copy.deepcopy(matriz)
    for i in range(len(matriz)):
        for j in range(len(matriz[i])):
            if grau == 90:
                nova_matriz[j][len(matriz)-(i+1)] = matriz[i][j]
            elif grau == 180:    
                nova_matriz[len(matriz)-(i+1)][len(matriz)-(j+1)] = matriz[i][j]
            elif grau == 270:  
                nova_matriz[len(matriz) - (j+1)][i] = matriz[i][j]
    return nova_matriz
    
if __name__ == '__main__':
        print(rodar([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[21,22,23,24,25]], 90))
Trata-se de um programa muito bom, denotando ter entendido bem onde estava a dificuldade maior. E a solução para o problema não era fácil de encontrar... Teve o cuidado de fazer uma cópia profunda, pois estamos a lidar com listas de listas, e calculou correctamente a relação entre as posições antigas e as novas. Mas no email que me mandou estava um pouco incomodado pois o programa só funcionava bem no caso das matrizes serem quadradas. Vamos então ajudá-lo a resolver este pequeno detalhe...

A chave para a solução consiste em perceber que o número de linhas e de colunas original pode mudar com a rotação. Por essa razão não podemos fabricar a nova matriz a partir da matriz inicial. Devemos criá-la de raíz. Uma possibilidade é criar a matriz com a nova estrutura e colocando no seu interior zeros. Com a estrutura depende do valor da rotação vamos começar por considerar o caso de uma rotação de 90º, na qual as linhas passam a colunas. Assim uma matriz mXn passa a nXm.
    nova_matriz = []
    for i in range(len(matriz[0])):
        nova_linha = []
        for j in range(len(matriz)):
            nova_linha.append(0)
        nova_matriz.append(nova_linha) 
Claro que já interiorizou o conceito de listas por compreensão pode chegar a uma solução mais curta:
nova_matriz = [[0 for j in range(len(matriz))] for i in range(len(matriz[0]))]  
Estamos em condições de escrever um programa que faça rodar uma matriz de 90º.
def rodar_90(matriz):
    nova_matriz = [[0 for j in range(len(matriz))] for i in range(len(matriz[0]))]  # <<------ Aqui está o segredo :-)
    for linha in range(len(matriz)):
        for coluna in range(len(matriz[0])):
            nova_matriz[coluna][len(matriz) - 1 - linha] = matriz[linha][coluna]
    return nova_matriz
Mas ainda falta o resto, 180º e 270º. Vamos a isso. Como a nossa solução autonomizou as rotações, vamos ter um programa principal como este:
def rodar(matriz,grau):
    if grau == 90:
        return rodar_90(matriz)
    elif grau == 180:
        return rodar_180(matriz)
    elif grau == 270:
        return rodar_270(matriz)
    else:
        print('Oops, isso ainda não sei fazer...!')
        return -1
O resto da solução é trivial: rodar 180 é o mesmo que rodar duas vezes 90; rodar 270 é o mesmo que rodar três vezes 90 ou rodar uma vez 180 e outra 90. Certo?
def rodar_180(matriz):
    return rodar_90(rodar_90(matriz))

def rodar_270(matriz):
    return rodar_90(rodar_180(matriz))
Moral da história. Partimos de uma boa solução mas que resolve apenas um caso especial do problema (matrizes quadradas), e identificámos onde estava o problema; resolvemos para um caso (90) e usamos essa solução para o resto (180, 270); pelo caminho ainda deparámos com o problema causado pela mutabilidade e partilha de memória (modo como criámos a nova matriz); e ainda apresentámos uma solução baseada em listas por compreensão. Mas o essencial da mensagem é: perante um problema difícil, tente resolver uma versão mais simples, decomponha esse problema em sub-problemas e depois generalize a solução para chegar ao resultado pretendido.

Sem comentários:

Enviar um comentário