A distância de edição (ou Levenshtein) entre duas seqüências é o número mínimo de inserções, exclusões e substituições de caracteres únicos necessárias para transformar uma sequência em outra. Se as duas seqüências tiverem comprimento n cada, é sabido que isso pode ser feito em O (n ^ 2) por programação dinâmica. O código Python a seguir executa esse cálculo para duas strings s1
e s2
.
def edit_distance(s1, s2):
l1 = len(s1)
l2 = len(s2)
matrix = [range(l1 + 1)] * (l2 + 1)
for zz in range(l2 + 1):
matrix[zz] = range(zz,zz + l1 + 1)
for zz in range(0,l2):
for sz in range(0,l1):
if s1[sz] == s2[zz]:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz])
else:
matrix[zz+1][sz+1] = min(matrix[zz+1][sz] + 1, matrix[zz][sz+1] + 1, matrix[zz][sz] + 1)
return matrix[l2][l1]
Nesta tarefa, você precisa se aproximar o máximo possível da distância de edição, mas com uma severa restrição de memória. É permitido ao seu código definir uma matriz contendo 1000 números inteiros de 32 bits e esse deve ser o único armazenamento temporário usado em seu cálculo. Todas as variáveis e estruturas de dados devem estar contidas nessa matriz. Em particular, você não seria capaz de implementar o algoritmo acima, como para cadeias de comprimento 1000, pois seria necessário armazenar pelo menos 1.000.000 de números. Onde sua linguagem não possui números inteiros de 32 bits (por exemplo, Python), basta garantir que você nunca armazene um número maior que 2 ^ 32-1 na matriz.
Você pode ler os dados usando qualquer biblioteca padrão de sua escolha sem se preocupar com as restrições de memória nessa parte. Para tornar a competição justa para a parte principal do seu código, você só pode usar operações que sejam funcionalmente equivalentes às da linguagem de programação C e não pode usar nenhuma biblioteca externa.
Para ficar mais claro, a memória para armazenar os dados de entrada ou usados pelo intérprete de seu idioma, JVM etc. não conta para o seu limite e você não pode gravar nada no disco. Você deve assumir que os dados de entrada são somente leitura quando estão na memória, para não poder reutilizá-los para ganhar mais espaço de trabalho.
O que eu tenho que implementar?
Seu código deve ser lido em um arquivo no seguinte formato. Terá três linhas. A primeira linha é a verdadeira distância de edição. O segundo é a string 1 e o terceiro é a string 2. Vou testá-lo com os dados de amostra em https://bpaste.net/show/6905001d52e8, onde as strings têm comprimento 10.000, mas não devem ser especializadas para esses dados. Ele deve gerar a menor distância de edição possível entre as duas strings.
Você também precisará provar que sua distância de edição é proveniente de um conjunto válido de edições. Seu código deve ter uma opção que o transforme em um modo que possa usar mais memória (o quanto você desejar) e produza as operações de edição que oferecem distância de edição.
Ponto
Sua pontuação será a (optimal edit distance/divided by the edit distance you find) * 100
. Para começar, observe que você pode obter uma pontuação apenas contando o número de incompatibilidades entre as duas cadeias.
Você pode usar qualquer idioma que desejar, disponível gratuitamente e fácil de instalar no Linux.
Desempate
No caso de um tie-break, executarei seu código na minha máquina Linux e o código mais rápido vence.
for(int i=0;i<=5;i++)
permitido porque está armazenando dadosi
?{ uint32_t foo[1000]; for (foo[0] = 0; foo[0] < 5; ++foo[0]) printf("%d ", foo[0]); }
Isto está assumindo que sua matriz de números inteiros de 32 bits será chamadafoo
.Respostas:
C ++, pontuação 92,35
Algoritmo de estimativa: o algoritmo encontra o primeiro lugar em que as duas cadeias diferem e, em seguida, tenta todas as N permutações de operação possíveis (inserir, excluir, substituir - caracteres correspondentes são ignorados sem consumir uma operação). Ele pontua cada conjunto possível de operações com base em quanto mais longe esse conjunto de operações corresponde às duas cadeias, mais o quanto isso faz com que os comprimentos das cadeias convergam. Depois de determinar o conjunto de N operações com maior pontuação, a primeira operação no conjunto é aplicada, a próxima incompatibilidade é encontrada e o processo se repete até o final da sequência.
O programa tenta todos os valores de N de 1 a 10 e seleciona o nível que deu os melhores resultados. N = 10 é geralmente o melhor agora que o método de pontuação leva em consideração o comprimento da string. Valores mais altos de N provavelmente seriam ainda melhores, mas levariam exponencialmente mais tempo.
Uso da memória: Como o programa é puramente iterativo, ele precisa de muito pouca memória. Apenas 19 variáveis são usadas para rastrear o estado do programa. Eles são definidos por #defines para atuar como variáveis globais.
Uso: O programa é usado da mesma forma que o feersum: o primeiro parâmetro é assumido como o arquivo e quaisquer parâmetros adicionais indicam que as edições devem ser mostradas. O programa sempre imprime a distância estimada de edição e a pontuação.
Saída de verificação: a saída de verificação formatada em três linhas:
A linha superior é a sequência de destino, o meio são as operações e a parte inferior é a sequência que está sendo editada. Os espaços na linha de operação indicam que os caracteres correspondem. 'R' indica que a sequência de edição tem seu caractere nessa posição substituído pelo caractere da sequência de destino. 'I' indica que a cadeia de edição possui o caractere da cadeia de destino inserido nessa posição. 'D' indica que a sequência de edição possui seu caractere nessa posição excluído. As cadeias de edição e de destino têm espaços inseridos quando o outro tem um caractere inserido ou excluído, para que se alinhem.
fonte
C ++ 75.0
O programa foi projetado para funcionar com seqüências de texto arbitrárias. Eles podem ter comprimentos diferentes, desde que não excedam 13824 caracteres. Ele usa 1.897 números inteiros de 16 bits, o que equivale a 949 números inteiros de 32 bits. No começo, eu estava escrevendo em C, mas depois percebi que não havia função para ler uma linha.
O primeiro argumento da linha de comando deve ser um nome de arquivo. Se existir um segundo argumento, um resumo das edições será impresso. A primeira linha do arquivo é ignorada, enquanto a segunda e a terceira são as seqüências de caracteres.
O algoritmo é uma versão duplamente bloqueada do algoritmo usual. Ele executa basicamente o mesmo número de operações, mas é obviamente muito menos preciso, pois se uma subsequência comum é dividida na borda de um bloco, grande parte da economia potencial é perdida.
fonte
Python,
100Consegui calcular perfeitamente a distância de edição no limite de memória alocado. Infelizmente, esta entrada viola duas regras do desafio, em letra, se não em espírito.
Primeiro, eu realmente não armazenei meus dados em 1000 ints de 32 bits. Para seqüências de 10000 caracteres, meu programa cria duas matrizes de 10000 elementos que conterão apenas +1, 0 ou -1. Com 1,558 bits por número ternário, seria possível compactar esses 20000 trits em 31700 bits, deixando 300 bits como mais do que suficiente para os meus 7 números inteiros de 16 bits restantes.
Segundo, não implementei o modo necessário para mostrar edições. Como alternativa, implementei um modo que imprime toda a matriz de edição. É absolutamente possível calcular o caminho de edição a partir dessa matriz, mas não tenho tempo agora para implementá-lo.
exemplo de entrada:
exemplo de saída detalhada:
fonte