Hoje resolvi um problema usando programação dinâmica: isso significa que tive que construir, inicializar e preencher uma matriz, de acordo com uma fórmula de recorrência.
O problema era, grosso modo, “calcular o número mínimo de substituições de caracteres necessárias para fazer uma string ‘válida’ da string de entrada”.
Meu algoritmo e código eram bons, mas quando executei o código, as respostas que obtive estavam obviamente incorretas. Para todas as entradas, obtive 0 como resposta. Eu sabia, a partir de casos de entrada triviais, que a resposta não era 0 para a maioria dos meus casos de teste.
Felizmente, tive o reflexo de imprimir a matriz inicial e final. E o que vi foi surpreendente: a matriz inicial estava errada !!!
Aqui está o código de inicialização que estava usando para a dp
matriz:
dp = (N+1)*[ 5*[N] ]
dp[0][0] = 0
onde N
é um inteiro estritamente positivo, o comprimento da string de entrada. A partir desse código de inicialização, eu esperava uma matriz onde todos os valores fossem iguais ao valor de N
, exceto para o valor na entrada [0][0]
. Por favor, pare aqui e dê seu melhor palpite sobre o que eu realmente tenho.
Tem um palpite? Bem, a seguinte sessão Python apresenta o problema:
>>> N = 2
>>> dp = (N+1)*[ 5*[N] ]
>>> dp
[[2, 2, 2, 2, 2], [2, 2, 2, 2, 2], [2, 2, 2, 2, 2]]
>>> dp[0][0] = 0
>>> dp
[[0, 2, 2, 2, 2], [0, 2, 2, 2, 2], [0, 2, 2, 2, 2]]
Como você pode ver, existem 0
s na primeira entrada de cada linha da matriz !! O que aconteceu?
Bem, neste ponto, você entendeu: o operador binário *
, que se aplica a um número inteiro, digamos k
, à esquerda, e uma lista, digamos L
, à direita, cria uma nova lista que consiste na k
concatenação de L
, e este nova lista contém referências aos mesmos objetos apontados pelas entradas da lista original.
Como você vê, a linha
>>> dp[0][0] = 0
acima está na verdade modificando o objeto apontado por , e , e isso explica o comportamento estranho do meu programa.dp[0]
dp[1]
dp[2]
Bem, à medida que o protocolo chega ao fim, apresento algumas conclusões. Estou muito feliz por ter impresso os dados no início do processo de depuração. Isso lançou muita luz sobre o que realmente estava acontecendo.
Agora, no que diz respeito ao próprio operador, minha conclusão é a seguinte: o operador ‘int * list’ só deve ser usado quando todos os elementos da lista são imutáveis, ou pelo menos somente leitura .
A propósito, finalmente usei a linha
dp = [ 5*[N] for _ in xrange(N+1) ]
dp[0][0] = 0
para inicializar minha matriz.