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