Estou lendo o artigo NJ Larsson, A. Moffat: Compactação baseada em dicionário offline , que descreve um algoritmo de compactação que, se bem entendi, é bastante semelhante à codificação de pares de bytes .
Dada uma string de comprimento , estou tentando entender como se pode compactá-la no tempo linear, , com esse método de compactação. Como exatamente isso é feito? Eu li o jornal, mas ainda não entendo como eles atingem o tempo linear, então talvez eu entenda isso explicado de uma maneira diferente.
Minha primeira confusão surge na primeira etapa do algoritmo, onde encontramos o par mais comum, por exemplo, no abcababcabc
par mais comum ab
seria substituído por um novo símbolo, digamos XcXXcXc
. Não entendo como podemos encontrar o par mais comum com rapidez suficiente. Minha abordagem ingênua seria olhar primeiro para o primeiro par ab
e depois contar o número de ocorrências, depois olhar para o próximo par bc
e contar o número de ocorrências etc. No entanto, isso já daria apenas para encontrando o par mais comum uma vez .
A seguir, mesmo que eu entenda como encontrar o par mais comum no tempo . Meu próximo problema é que, não precisamos encontrar o par mais comum até vezes? E, portanto, isso daria um tempo total de ?
Respostas:
Verifiquei o código, reli o artigo e parece que o algoritmo não está executando em da maneira que você descreveu. As chamadas recursivas para encontrar pares funcionam emO(n)
O(nlogn) dividido por constante se você quiser empacotá-lo até o fim. De fato, a estrutura abaixo mantém ponteiros para a primeira ocorrência de pares, divide a fila de pares por ocorrências para torná-la eficiente, mas ainda linear. Por outro lado, analisando o código escrito por Ph.D. aluno do autor, descobri vários truques - o comprimento dos pares (o nome original) é dado como parâmetro (o padrão é 2) e a recorrência é limitada pelo parâmetro, que pode descartar chamadas adicionais (o padrão é de cerca de 5 níveis), com capacidade de fazer apenas um passe ou empurrar até o fim. O código padrão é executado em , mas não produz uma compactação ideal. Há também opções para preferir memória, hora ou ser neutro.O(n)
O documento e o código mostram o uso da tabela de hash, que é executada apenas no tempo constante esperado, mas a função fornecida funciona muito bem com caracteres. Além disso, o código limita o comprimento da entrada por código constante constante.
Mudar a hashtable para a árvore de sufixos e reescrever a parte da recorrência em pares parciais de contabilidade pode gerar melhores resultados - não tenho certeza se isso é possível torná-lo , essa provavelmente seria uma pergunta diferente.O(n)
Também existe um truque usado para impedir que o espaço vazio seja percorrido - o início do bloco vazio aponta para o primeiro caractere não vazio, mas isso apenas acelera o deslocamento, ainda com tempo linear. A parte responsável por esse comportamento é substituir pares pelo novo personagem a cada iteração - manter apenas regras de reescrita e operar com elas seria mais rápido, mas ainda assim o acesso para verificar se novos pares foram introduzidos exigiria a verificação de regras de reescrita nos novos - no caso, construímos uma árvore binária completa com pares nas folhas, de forma que, após substituir o par por um novo caractere e concatenar com o pai, obtivemos novos pares nas folhas até a raiz - existem bate-papo então pares, então ,n n2 n4 n8 , ... Você vê o padrão, existem pares, mas de acordo com algoritmo descrição deste produtividade do feijoeiro Ter tempo apenas passagens de entrada.n16 2n O(nlogn+n)
fonte