3º
Trabalho Prático
marcus@limiar.com.br - http://www.limiar.com.br
16 de
Julho de 2002
Projeto e Análise de Algoritmos
1º Semestre de 2002
I) Apresentação:
O objetivo deste trabalho prático
é avaliar diferentes algoritmos de casamento de padrões, tanto sob um ponto de
vista teórico, pela análise de complexidade de algoritmos, quanto de um ponto
de vista prático, pela avaliação e comparação empírica dos algoritmos e entre
si, com o AGREP [AGREP], e os programas da família GREP [GREP], programas tradicionais, disponíveis para uso em
diversos ambientes.
II) Algoritmos e Estruturas de Dados:
II.1) Algoritmo
Boyer-Moore-Horspool – BMH:
Algoritmos de casamento de padrões
buscam, num texto de tamanho n, as diferentes ocorrências de um padrão
de tamanho m. O algoritmo mais simples para o casamento de padrões usa o
processo de força bruta: a idéia básica é comparar cada substring de
tamanho m com o padrão. Um algoritmo deste tipo seria como o que se
segue:
(1) void ForcaBruta
(texto, n, padrao, m) {
(2) int i, j,
k;
(3) char *texto, *padrao;
(6) for (i=0; i<n-m+1; i++) {
(7) for (j=0, k=i; (j<m) &&
texto[k]==padrao[j]; j++) k++;
(8) if (j>m) printf(“Padrao achado em %d\n”,i);
(9) }
(10)}
A idéia básica do algoritmo BMH é otimizar as linhas (6) e (7), observando que, ao caminhar na comparação do texto com o padrão, o algoritmo obtém informação sobre o texto que está sendo pesquisado. O algoritmo pode tirar partido desta informação para que, uma vez que ocorra uma falha numa comparação em (7), a linha (6) possa passar para um ponto mais à frente no texto, em lugar de avançar de um em um (i++).
A estrutura geral do algoritmo BMH é a seguinte:
(1) void BMH
(texto, n, padrao, m) {
(2) int i, j,
k;
(3) char *texto, *padrao;
(4) for( k=0;
k<ALPHABET_SIZE; k++ ) skip[k]= m;
(5) for( k=0;
k<m-1; k++ ) skip[pat[k]]= m-k-1;
(6) for(
i=m-1; i<n; i+=skip[texto[i]]) {
(7) for(j=m-1, k=i; j>=0 &&
texto[i]==padrao[j]; j--) k--;
(8) if (j==(-1)) printf(“Padrao achado em
%d\n”,i);
(9) }
(10)}
Neste algoritmo, ALPHABET_SIZE
indica o número de caracteres no alfabeto, texto é o texto
no qual o padrão padrao é pesquisado, n é o
tamanho do texto, e m
o tamanho do padrão. O vetor skip é usado
para controlar os saltos adiante no texto, quando ocorre uma falha na
comparação do padrão. As linhas (4) e (5) fazem a inicialização de skip.
O núcleo do algoritmo BMH, nas
linhas (6) a (9) é muito similar ao algoritmo
ForcaBruta, mas contém as otimizações que o tornam mais eficiente. A
linha (6) de BMH varre o texto. Note que, ao contrário da linha (6) de
ForcaBruta, o caminhamento no texto se inicia na posição m-1, porque
BMH faz as comparações da direita para a esquerda, e m-1 aponta
para o final do padrão. Além disto skip é usado
para avançar i mais rapidamente que na ForcaBruta.
A linha (7) de BMH é similar à
linha (7) de ForcaBruta . A principal diferença é que, em BMH, o caminhamento
se faz da direita para a esquerda. Portanto, os limites do for
se iniciam à direita do padrão, e caminham em direção à esquerda. Por
fim, o teste de padrão encontrado, na linha (8), passa a verificar se a
variável de controle transbordou para a esquerda, e não para a direita,
já que ela assume, em BMH, valores decrescentes.
O grande segredo reside, então no
cálculo e preenchimento prévios do vetor skip, que
controla os saltos adiante no texto. Este cálculo é baseado na heurística de ocorrência,
segundo a qual, na ocorrência de uma falha, deve-se alinhar o caractere do
texto que causou a falha com o primeiro caractere do padrão que combine com o
mesmo. No caso deste caractere não estar presente no padrão, pode-se alinhar o
padrão imediatamente após o caractere que causou a falha. Formalmente:
skip[x]
= { min ( s | s=m+1 ou (1 <= s <=
m e padrao[m+1-s] = x) }
A linha (4) preenche skip com o
valor inicial m+1. Em seguida, a linha (5) varre skip da
esquerda para a direita, preenchendo-o com valores cada vez menores (m-k-1,
k crescente) para cada caractere do padrão.
II.2) Algoritmo Wu-Manber:
O algoritmo Wu-Manber [WU92] deriva do algoritmo ShiftOr, proposto por Baeza-Yates
e Gonnet em [GBY89]. A idéia fundamental é representar o padrão a
pesquisar como um autômato finito. Neste autômato, as transições ocorrem quando
se aceita um caractere do texto que case com um caractere do padrão, e os
estados indicam quais caracteres já foram recebidos até o momento. Quando se
atinge um estado final, o padrão foi encontrado.
A eficiência do algoritmo se deve
à implementação projetada por seus autores: os estados, e os estados
alcançáveis são representados por máscaras de bits, toda a manipulação de
estados se fazendo por meio de operações lógicas e aritméticas. Então, se
usarmos padrões pequenos, poderemos representar as máscaras e estados
alcançáveis como palavras da arquitetura alvo onde o algoritmo for
implementado.
Certamente esta abordagem
restringe a dimensão dos padrões que podem ser pesquisados. Os autores do
algoritmo argumentam que, no caso de manipulação de textos, esta limitação não
reduz a utilidade do algoritmo, porque, freqüentemente, os padrões buscados são
pequenos.
Este algoritmo também é capaz de
lidar com busca aproximada, que permite erros. A implementação da busca
aproximada, aceitando erros é relativamente simples. Basta estender o autômato
para incluir transições espontâneas. Cada transição espontânea indicará um
erro: troca ou supressão de caractere. A figura abaixo, extraída das notas de
aula, mostra uma visão esquemática de um autômato para o padrão ABCA aceitando
até dois erros:
A estrutura geral do algoritmo é a seguinte:
( 1) void
WM_Exec(p, m, t, n, k) {
( 2) int i,
j,
( 3) R0[MAX_NUMBER_OF_ERRORS],
( 4)
R1[MAX_NUMBER_OF_ERRORS],
( 5) S
[ALPHABET_SIZE],
( 6)
Start[MAX_NUMBER_OF_ERRORS]
( 8) for (i =
0; i <= k; i++) {
( 9)
Start[i] = 0;
(10) for (j
= 0; j <= i; j++)
(11)
Start[i] |= 1 << (m - j);
(12) }
(13) for (i =
0; i < ALPHABET_SIZE; i++)
(14) S[i] =
0;
(16) for (i =
0; i < m; i++) { {
(17)
S[p[i]] |= 1 << (m - 1 - i);
(19) }
(20) for (i =
0; i <= k; i++) {
(21) R1[i]
= Start[i];
(22) R0[i]
= Start[i];
(23) }
(24) for (i = 0; i < n; i++) {
(25) R1[0] =((R0[0] >> 1)& S[t[i]])
| (Start[0] & (R0[0]);
(26) for (j
= 1; j <= k; j++) {
(27) R1[j] = ((R0[j] >> 1) &
S[t[i]]) |
(28) ((R0[j-1] |
((R1[j-1]|R0[j-1])>>1)) ) |
(29)
(Start[0] & R0[j]);
(30) }
(31) for (j
= 0; j <= k; j++)
(32) R0[j] = R1[j];
(33) if (R1[j]&1) printf(“Padrao achado em %d\n”,i);
(34) }
(35) }
(36) }
Neste algoritmo, as linhas de (8) a (23) efetuam inicializações, e as linhas de (24) a (35) efetuam a busca, ALPHABET_SIZE indica o número de caracteres do alfabeto, p é o padrão, e m seu tamanho, t é o texto, e n seu tamanho, k é o número de erros a aceitar. R0 indica o estado anterior, e R1 o atual. Start contém o valor inicial deste estado, calculado na inicialização. Todos os três são vetores, com uma posição para cada número de erros a aceitar (0=sem erros, 1=1 erro, k=k erros). S é um vetor de máscaras, uma para cada caractere do alfabeto, e que indica onde cada caractere ocorre no padrão.
As linhas (8) a (12) calculam o
valor de Start, que será
usado para inicializar R0 e R1 nas
linhas (20) a (23). Estas linhas calculam, para cada número de erros a aceitar,
o valor inicial do estado do algoritmo, da seguinte forma:
·
Para busca exata (sem erros, k=0), o m-ésimo bit em 1. Ex:
para m=3, teremos Start[0] = 8 (1000 em binário);
·
Para busca com erros, os k-ésimos bits à Start[i] em 1, a
partir do m-ésimo, para cada 1<= i <=k. Ex: para m=3, Start[1]= 12 (1100
em binário), Start[2]= 14 (1110 em binário).
As linhas (13) a (19) calculam S, vetor de
máscaras que indicam, para cada caractere do alfabeto, em que posição do padrão
o caractere se encontra.
Nas linhas (24) a (36) está o
núcleo do algoritmo. R0 (estado anterior), e R1
(estado atual) são registradores contém um bit para cada estado do
autômato correspondente ao padrão. Cada bit em 1 indica quais estados podem ser
alcançados a partir do estado atual. Um novo estado é calculado a partir do
estado anterior, dado por R0, do estado atual, dado por R1
e das posições em que o caractere em exame ocorre no padrão, dadas por S:
·
Na linha (25) calcula-se o novo estado do registrador para
casamento sem erros, Este valor é obtido verificando-se se o próximo estado
poderá ser ativado pelo caractere corrente;
·
Nas linhas (26) a (30) faz-se o cálculo dos novos estados
dos registradores para cada número de erros. Para tanto, verifica-se se o
próximo estado poderá ser ativado pelo caractere corrente (27), por transição
expontânea a partir do estado anterior com remoção, adição ou substituição de
um caractere.
III) Análise de Complexidade:
III.1) Algoritmo
Boyer-Moore-Horspool – BMH:
Sejam n o tamanho do texto, m o tamanho do padrão e c a dimensão do alfabeto. A complexidade de inicialização leva em conta as linhas (4) e (5) do algoritmo. A linha (4) tem sua complexidade dependente da dimensão do alfabeto, e o custo de execução da linha (5) depende diretamente do tamanho do padrão. O custo total de inicialização será, então, O(c) + O(m) = O(m + c).
Em termos de espaço, temos o custo para armazenar o vetor de deslocamentos skip: O(c).
O custo de execução depende da probabilidade de se encontrar o padrão pesquisado, de sua localização, da freqüência com que ocorre. Também depende da própria estrutura do padrão, e também da estrutura do texto, que afetam a possibilidade de se efetuarem os saltos responsáveis pelo bom desempenho do algoritmo.
Se considerarmos um caso em que o padrão tenha todos os caracteres iguais, e que o texto somente contenha apenas este mesmo caractere:
· Não ocorrerão saltos, e a linha (6) será executada n-m+1 vezes.
· A linha (7) será executada m vezes.
· Serão executadas nm-m2+m comparações. Considerando que m é muito menor que n, temos o pior caso apresentado em [FBY], que é O(n*m).
A análise do custo esperado exige a elaboração de um modelo probabilístico, como o que é apresentado em [BM] e em [BGM], e que permitem chegar a um custo médio, para m muito menor que n, de O(n/m).
III.2) Algoritmo Wu-Manber:
Sendo k o número de erros aceito, e c a dimensão do alfabeto, a complexidade em espaço, conforme as linhas (3) a (6) é 3k+2c.
Sendo m o tamanho do padrão, a complexidade de inicialização é:
· Linhas (8) a (12): (k+1) + (k+1)(k+2)/2 = (k+1) +(k2 + 3k + 2)/2 = k2/2+ 5k/2 + 2
· Linhas (13) a (14): c
· Linhas (16) a (19): m
· Linhas (20) a (23): 2k+2
· Complexidade de inicialização: k2/2+ 9k/2 + 4 + c + m
Sendo n o tamanho do texto, w o tamanho da palavra do computador alvo, e sendo m < w, a complexidade de execução é:
· Linha (24): n
· Linha (25): O(1)
· Linhas (26) a (34): 2*O(k) = O(k)
· Complexidade de execução: O(n*k)
IV) Avaliação Empírica:
Algoritmos
e programas avaliados: Em nossa avaliação, efetuamos diversos experimentos e medições, a fim
de montar um panorama consistente e amplo das características e desempenho dos
algoritmos implementados (Wu-Manber e Boyer-Moore-Horspool), e de seu
comportamento quando comparado a dois programas disponíveis para uso geral, o AGREP
[AGREP] e os programas da família GREP [GREP].
Ambiente
de testes: Os
experimentos foram feitos num notebook Toshiba Satellite S201, com cpu
Pentium de 1,6GHz e 256MB de
memória, executando Windows XP Professional. O ambiente de software usado
foi usado o ambiente Cygwin [CYGWIN].
Massa
de dados para testes: A massa de dados empregada foi a indicada na proposição do trabalho
prático [[TP3]], e disponível na rede do DCC. Esta massa de dados
inclui arquivos de 2, 10, 20 e 100 MB.
Modalidade
de medição de tempo adotada: As medições de tempo consideraram o tempo decorrido.
Embora este tipo de medição implique em certa imprecisão na medida, ele permite
considerar os tempos de execução dos algoritmos, e também os tempos de entrada
e saída que, no caso de pesquisa em arquivos, são componentes muito
significativos do tempo total de execução[1].
Foram
efetuados experimentos com relação aos tempos de entrada e saída, tanto para a
leitura de arquivos, quanto para a emissão de resultados. Com isto, pudemos
avaliar a influência destes tempos nos resultados encontrados.
IV.1) Medições de Entrada e Saída:
O gráfico Tempo de Leitura de
Arquivos mostra as medições efetuadas com a leitura dos mesmos arquivos
empregados nos testes de desempenho dos algoritmos. Foram efetuados testes com
dois tipos de leitura:
·
Leitura blocada, com registros de tamanho fixo, e chamadas open,
read, close;
·
Leitura com stream, com chamadas fopen, fgets,
fclose, que trata registros de tamanho variável, delimitados por “\n” (Os
arquivos da massa de testes são estruturados desta forma).
Os tempos obtidos mostram um fato
notável: a leitura blocada se mostrou ligeiramente mais lenta que a leitura
stream. Acreditamos que estes resultados estejam muito ligados a
peculiaridades de sistema operacional e compilador (como é típico das
implementações de mecanismos de entrada e saída). Portanto, ao portar o software
para outras plataformas, torna-se muito importante repetir estas medições, a
fim de usar o mecanismo mais adequado à plataforma alvo.
Por fim, podemos considerar os
tempos de leitura por stream como um limite inferior para o tempo de
execução de todos os algoritmos testados. Em particular, podemos considerar o
tempo de cerca de 6s como um limite inferior para algoritmos de busca no
caso do arquivo wsj88, de cerca de 100 MB
IV.2)
O programa desenvolvido - RGREP:
Para
a efetivação dos testes, foi desenvolvido o programa RGREP. O programa usa
algoritmos disponíveis em [[TP3]] e [GBY]. Este programa seleciona o algoritmo de pesquisa
conforme o tipo de pesquisa solicitada (com ou sem erros). Foram incluídas
diversas facilidades no programa que o tornam muito similar a um programa útil
e competitivo na vida real. Foram tomados diversos cuidados na implementação
para evitar falhas e comportamentos que dificultariam o uso efetivo do
programa.
Dentre
os pontos levados em conta na implementação, e que tornam o programa
competitivo, citamos:
·
Interface
compacta, de linha de comando, padrão em aplicativos e ferramentas Unix,
incluindo opções de ativação similares às dos aplicativos GREP e AGREP, capaz
de tratar arquivos na entrada padrão, e que inclui uma opção de ajuda (que
explica de forma sucinta o funcionamento do programa).
·
Cuidados
para evitar erros de buffer overflow, especialmente na alocação e
manipulação de registros e argumentos, causa freqüente de falhas inesperadas e
falhas de segurança nos mais variados sistemas.
·
Estruturação
e documentação cuidadosas do código, como forma de aumentar legibilidade de
manutenibilidade.
Embora
acreditemos que o programa possa ser usado efetivamente, certos pontos da
implementação não foram abordados em profundidade, e devem ser revistos:
·
Como
se verá adiante, a implementação de AGREP para busca com erros é muitíssimo
mais eficiente. Isto mostra que a implementação que usamos para a pesquisa com
erros (Wu-Manber em [TP3]) deve ser revista.
·
A
pesquisa de múltiplos padrões (opção –f) foi implementada de forma
ineficiente, que requer o re-processamento de padrões cada vez que se lê uma
linha do arquivo alvo. Esta implementação pode ser facilmente modificada para
evitar este re-processamento. Também é importante avaliar versões de algoritmos
especialmente adaptadas para a busca de múltiplos padrões. Variações de
algoritmos para casamento de múltiplos padrões podem ser vistos em [WU94] e em [CW].
·
O
processo de leitura de arquivos não faz uso de nenhuma otimização nem de
leitura antecipada de dados. A grande diferença de tempo que observamos entre
nosso programa, quando efetuando busca com erros, e AGREP, sugere que este
aspecto deva ser investigado.
·
O
programa AGREP limita a busca com erros a um máximo de 8 erros. Isto sugere que
exista alguma otimização, ou melhoria de desempenho significativa, para
operações em 1 byte, o que deve ser verificado.
·
Uma
extensão importante é permitir a especificação de padrões como expressões
regulares.
·
Seria
interessante avaliar o variação do algoritmo BMH proposta em [SUNDAY], que mais compacta, e apresenta algumas otimizações
em relação à versão usada por RGREP.
Desempenho: Os
gráficos RGREP-Wu-Manber e RGREP-
BMH x Wu-Manber exato mostram o desempenho global do programa para diversas
situações. Foram efetuadas buscas com strings de 3, 8 e 23 bytes,
e com 1, 4, 8 e 28 erros. Os gráficos mostram que, como esperado, os tempos de
execução crescem linearmente com relação ao tamanho do padrão, com relação ao
número e erros, e com relação ao tamanho do arquivo.
No caso de Wu-Manber, se
considerarmos linear a relação entre número de erros e tempo de execução, teremos:
Então, a pesquisa com 28 erros
pode ficar inviável para arquivos de 1 GB (cerca de 1800 segundos), mas viável
para 1 erro (cerca de 100 segundos).
No caso de Boyer-Moore-Horspool, o
tempo é menor para padrões maiores:
A
pesquisa sem erros é, então, viável mesmo para arquivos da ordem de 1GB (cerca
de 70 segundos para padrões de 3 bytes)
IV.3) Comparação com GREP:
Foram efetuadas buscas com padrões
de 3, 8 e 23 bytes. Os gráficos a seguir apresentam os resultados obtidos.
Estes gráficos mostram que o tempo de execução de RGREP para pesquisa exata, com o algoritmo Boyer-Moore-Horspool, depende linearmente do tamanho do arquivo. Se desprezarmos os experimentos com padrões de 8 bytes, teremos uma confirmação de que o tempo é inversamente proporcional ao tamanho do padrão. Em síntese, confirma-se que o caso médio é O(mn), onde m é o tamanho do padrão, n o tamanho do arquivo, e n muito maior que m.
A busca exata com Wu-Manber também cresce linearmente com respeito ao tamanho do arquivo, e é muito eficiente. Os experimentos não permitem uma conclusão segura sobre a relação entre tamanho do padrão e o tempo de execução.
Por fim, pode-se concluir que as implementações de busca exata de RGREP são muito competitivas e freqüentemente mais rápidas que a versão de GREP avaliada.
IV.4) Comparação com AGREP:
Foram efetuadas buscas com 1, 4 e
8 erros (o limite de AGREP para buscas aproximadas é de 8 erros), e buscas
exatas com padrões de 3, 8 e 23 bytes. O gráfico a seguir apresenta os
resultados obtidos na busca com erros. Os três gráficos anteriores, em IV.3,
incluem os resultados para busca exata.
Programa
AGREP: As
comparações demonstram as qualidades do AGREP. Seus resultados são muito
competitivos tanto para a busca com erros, quanto para a busca exata. No caso
da busca com erros, os resultados mostram claramente que seu tempo de execução
depende linearmente do tamanho do arquivo.
O
resultados também mostram que existe alguma dependência entre o número de erros
e o tempo de execução, mas os experimentos são insuficientes para caracterizar
perfeitamente qual a dependência. Aparentemente, o tempo de execução é
diretamente proporcional ao número de erros, o que confirma os resultados
teóricos em [WU92] (O(nk)).
O
resultado de AGREP é ainda mais surpreendente se compararmos seu tempo de
execução com o tempo para leitura de arquivos com stream, mostrado em IV.1.
Seu tempo de execução está muito próximo ao ali calculado. Este fato, aliado ao
fato de que o limite de erros de AGREP para busca aproximada é 8, sugere que
ele use algum tipo de otimização baseada em operadores de byte.
Programa RGREP para busca
aproximada: Os gráficos mostram que o crescimento do tempo de execução é
diretamente proporcional ao tamanho do arquivo e ao número de erros.
A busca aproximada é a situação em
que o programa RGREP, usando o algoritmo Wu-Manber, exibe seu pior
desempenho, muito inferior ao de AGREP.
De fato os tempos de execução tornam o programa inadequado para arquivos muito
grandes (conforme mostrado em IV.2, o tempo previsto para uma busca com
28 erros num arquivo de 1 GB é de cerca de 1800 segundos).
Programa RGREP para busca exata: Os
gráficos mostram que o crescimento do tempo de execução de RGREP com algoritmo
Boyer-Moore-Horspool para busca exata, é diretamente proporcional ao tamanho do
arquivo e ao número de erros. Também mostram que o tempo é inversamente
proporcional ao tamanho do padrão, o que é esperado (O(n/m), conforme
notas de aula).
A busca exata em RGREP com Wu-Manber também tem tempo de execução diretamente proporcional ao tamanho do arquivo. Não foi possível determinar qualquer relação entre tempo de execução e tamanho do padrão.
Por fim, a busca exata com RGREP é
competitiva, quando comparada com AGREP.
IV.6) Sabores de GREP: O
utilitário GREP, tradicional em sistemas Unix e Linux, e disponível para download
em [GREP], pode ser invocado de três maneiras distintas:
·
GREP: A versão tradicional, capaz de casamento de padrões
com expressões regulares;
·
EGREP: Uma versão estendida, capaz de aceitar, em sua linha
de comando, múltiplas expressões regulares;
·
FGREP: Uma versão reduzida, capaz de tratar padrões fixos,
sem expressões regulares, similar a RGREP.
O sabor escolhido para as
comparações foi GREP. Mostramos, abaixo, um gráfico com tempos de execução dos
três sabores que mostra que, embora GREP nem sempre seja o sabor
mais eficiente, todos são muito similares entre si:
IV.7) Tabelas de dados: A seguir,
temos as tabelas de dados usadas para gerar os gráficos acima.
Tempo de
leitura de arquivos |
|
|
||
Arquivo |
KBytes |
Linhas |
Stream |
Blocado |
wsj89 |
2.756 |
43.494 |
0,25 |
0,4 |
wsj88_10 |
9.901 |
140.539 |
0,73 |
0,6 |
wsj88_20 |
19.804 |
278.933 |
1,16 |
1,21 |
wsj88 |
106.885 |
1.523.893 |
5,77 |
7,4 |
Comparação entre RGREP e AGREP, aceitando erros |
|||||||||
Arquivo |
KBytes |
Linhas |
RGREP 1 erro |
AGREP 1 erro |
RGREP 4 erros |
AGREP 4 erros |
RGREP 8 erros |
AGREP 8 erros |
RGREP 28 erros |
wsj89 |
2.756 |
43.494 |
0,57 |
0,34 |
0,89 |
0,25 |
1,52 |
0,74 |
5,29 |
wsj88_10 |
9.901 |
140.539 |
1,08 |
0,64 |
3,01 |
0,61 |
5,24 |
1,2 |
17,85 |
wsj88_20 |
19.804 |
278.933 |
2,19 |
1,15 |
5,92 |
1,2 |
10,42 |
2,19 |
35,52 |
wsj88 |
106.885 |
1.523.893 |
10,74 |
6,05 |
31,7 |
5,94 |
56,95 |
11,27 |
191,74 |
Comparação
entre RGREP e AGREP, padrão de 3 bytes |
||||||||
Arquivo |
KBytes |
Linhas |
RGREP |
AGREP |
GREP |
FGREP |
EGREP |
RGREP-WM |
wsj89 |
2.756 |
43.494 |
0,4 |
0,44 |
0,35 |
0,42 |
0,46 |
0,19 |
wsj88_10 |
9.901 |
140.539 |
0,55 |
0,53 |
0,63 |
0,53 |
0,53 |
0,63 |
wsj88_20 |
19.804 |
278.933 |
1,43 |
1,17 |
1,21 |
1,46 |
1,37 |
1,21 |
wsj88 |
106.885 |
1.523.893 |
7,29 |
6,76 |
7,09 |
7,19 |
6,8 |
6,51 |
Comparação
entre RGREP e AGREP, padrão de 8 bytes |
||||||||
Arquivo |
KBytes |
Linhas |
RGREP |
AGREP |
GREP |
FGREP |
EGREP |
RGREP-WM |
wsj89 |
2.756 |
43.494 |
0,22 |
0,4 |
0,38 |
0,19 |
0,22 |
0,29 |
wsj88_10 |
9.901 |
140.539 |
0,68 |
0,54 |
0,59 |
0,67 |
0,54 |
0,56 |
wsj88_20 |
19.804 |
278.933 |
1,17 |
1,2 |
1,26 |
1,19 |
1,27 |
1,29 |
wsj88 |
106.885 |
1.523.893 |
6,71 |
6,31 |
6,71 |
6,45 |
6,4 |
6,36 |
Comparação
entre RGREP e AGREP, padrão de 23 bytes |
||||||||
Arquivo |
KBytes |
Linhas |
RGREP |
AGREP |
GREP |
FGREP |
EGREP |
RGREP-WM |
wsj89 |
2.756 |
43.494 |
0,32 |
0,33 |
0,38 |
0,23 |
0,19 |
0,22 |
wsj88_10 |
9.901 |
140.539 |
0,62 |
0,54 |
0,59 |
0,57 |
0,58 |
0,57 |
wsj88_20 |
19.804 |
278.933 |
1,64 |
1,41 |
1,26 |
1,13 |
1,15 |
1,12 |
wsj88 |
106.885 |
1.523.893 |
6,42 |
5,77 |
6,71 |
5,93 |
5,91 |
6,37 |
RGREP - Geral |
|||||||
BMH |
Arquivo |
KBytes |
Linhas |
3 bytes |
8 bytes |
23 bytes |
|
|
wsj89 |
2.756 |
43.494 |
0,4 |
0,22 |
0,32 |
|
|
wsj88_10 |
9.901 |
140.539 |
0,55 |
0,68 |
0,62 |
|
|
wsj88_20 |
19.804 |
278.933 |
1,43 |
1,17 |
1,64 |
|
|
wsj88 |
106.885 |
1.523.893 |
7,29 |
6,71 |
6,42 |
|
|
|
|
|
|
|
|
|
WU |
Arquivo |
KBytes |
Linhas |
1 erro |
4 erros |
8 erros |
28 erros |
|
wsj89 |
2.756 |
43.494 |
0,57 |
0,89 |
1,52 |
5,29 |
|
wsj88_10 |
9.901 |
140.539 |
1,08 |
3,01 |
5,24 |
17,85 |
|
wsj88_20 |
19.804 |
278.933 |
2,19 |
5,92 |
10,42 |
35,52 |
|
wsj88 |
106.885 |
1.523.893 |
10,74 |
31,7 |
56,95 |
191,74 |
|
|
|
|
|
|
|
|
WU-EXATO |
Arquivo |
KBytes |
Linhas |
3 bytes-WM |
8 bytes-WM |
23 bytes-WM |
|
|
wsj89 |
2.756 |
43.494 |
0,19 |
0,29 |
0,22 |
|
|
wsj88_10 |
9.901 |
140.539 |
0,63 |
0,56 |
0,57 |
|
|
wsj88_20 |
19.804 |
278.933 |
1,21 |
1,29 |
1,12 |
|
|
wsj88 |
106.885 |
1.523.893 |
6,51 |
6,36 |
6,37 |
|
BIBLIOGRAFIA:
[AGREP] http://www.tgries.de/agrep/
[BGM] Baeza-Yates,
R.A., Gonnet, G.H., Régniert, M., Analysis of Boyer-Moore-Type String
Searching Algorithms, Proceedings of the first annual ACM-SIAM symposium on
Discrete algorithms, Januarry 1990.
[BM] Boyer,
R. S., Moore, J. S., A Fast String Searching Algorithm, CACM, V20(10),
762-772, 1977.
[CW] Commentz-Walter, B., A string matching algorithm
fast on the average. In ICALP,
Lecture Notes in Computer Science, vol. 6. Springer-Verlag, 1979, pp. 118-132.
[FBY] Frakes, W. B. e Baeza-Yates (Eds) Information Retrieval Data Structures
and Algorithms. Prentice Hall, 1992
[GBY] http://www.dcc.uchile.cl/~rbaeza/handbook/
algs/7/713b.srch.c.html
[GBY89] G. H.
Gonnet e R. Baeza-Yates, A New Approach to Text Searching, ACM SIGIR Forum, Proceedings of the twelfth annual
international ACMSIGIR conference on Research and development in information
retrieval May 1989.
[GBY91] G. H.
Gonnet e R. Baeza-Yates, Handbook of Algorithms and Data Structures,
Addison-Wesley, 1991, 2ª edição.
[CYGWIN] http://www.cygwin.com
[GREP] http://www.gnu.org/software/grep/grep.html
[SUNDAY] D. M.
Sunday, A Very Fast Substring Search Algorithm, CACM, 33(8), 132-42,
1990
[TP3] http://www.dcc.ufmg.br/~nivio/cursos/pa02/tp3/pa02tp3.html
[WU92] S.
Wu, U. Manber, Fast Text Searching Allowing Errors, CACM, October 1992,
Vol35 No 10
[WU94] S.
Wu, U. Manber, A Fast Algorithm for Multi-Pattern Search, Technical Report TR-94-17,
Department of Computer Science, University of Arizona, 1994
[1] O mau uso de recursos de entrada e saída pode obscurecer a qualidade de um algoritmo eficiente. No entanto, como medida de qualidade final, do ponto de vista de usuário, acreditamos que o tempo efetivo de resposta seja mais significativo que a qualidade e eficiência internas de um programa. Portanto, acreditamos que a medida de tempo decorrido seja por demais significativa, e não possa, portanto, ser deixada de lado.