Universidade Federal de Minas Gerais - UFMG

Instituto de Ciências Exatas - ICEx

Departamento de Ciência da Computação

Disciplina: Projeto e Análise de Algoritmos

Professor: Nivio Ziviani

Aluna: Cristiana Maria Nascimento Gomes

 

 

Trabalho Prático 3

 

 

 

 

            O objetivo deste trabalho é projetar e implementar um sistema de programas para recuperar ocorrências de padrões em arquivos constituídos de documentos, utilizando algoritmos lineares de busca seqüencial.

            O sistema de programas recebe do usuário uma cadeia de caracteres (padrão), se a busca é exata (k=0) ou aproximada (0 < k < m), e imprime todas as ocorrências do padrão no texto. Foram utilizados os seguintes algoritmos: 

Algoritmo de Boyer-Moore-Horspool (BMH) e Baetas (Shift-And) para casamento exato de padrões.

Algoritmo proposto por Wu e Manber (extensão do Shift-And) para casamento aproximado de padrões.

 

P = vetor que contém os caracteres do padrão

Pi = posiçao i de P

m = tamanho do padrão

T =  texto no qual se procura o padrão

k = número de erros admissíveis

            Para os teste realizados deste trabalho foram utilizados os arquivos de documentos da coleção TREC disponível em export/texto2/wsj, com as seguintes características:

Coleção            Tamanho (Mb) 

wsj88                109      

wsj88_10          10        

wsj88_20          20        

wsj89                2.8       

 

 

1. Explicação detalhada dos algoritmos e estruturas de dados utilizados para resolver o problema

 

Algoritmo de Boyer-Moore-Horspool (BMH)

 

            O algoritmo de BMH é uma simplificação do algoritmo de Boyer-Moore (BM). O BM trabalha com duas heurísticas: match heuristic e a ocurrence heuristic, e funciona da seguinte maneira: posiciona o padrão sobre o caracter mais a esquerda do texto e tenta casar da direita para esquerda. Se nenhum erro ocorre, então o padrão foi encontrado. Caso contrário o algoritmo calcula um deslocamento de forma que o padrão é movido para uma posição a direita antes que seja possível existir um casamento exato.

            O BM tem uma versão simplificada SBM que usa somente a ocurrence heuristic. Essa heurística é utilizada para o cálculo do deslocamento. Quando um erro ocorre a ocurrence heuristic usa a informação sobre onde o bad character (caractere que causou o erro) do texto ocorre no padrão para propor um novo deslocamento (d). A principal justificativa para esta versão é que, na prática, padrões não são periódicos. BMH foi apresentado por Horspool, que notou que qualquer caractere do texto pode ser usado para endereçar a tabela de ocorrências (d). Baseado nisto, usou o algoritmo SBM e endereça a tabela d  com o caractere no texto correspondente ao último caractere do padrão. Para evitar comparação quando o valor na tabela é zero (último caractere do padrão), foi atribuído o valor m (tamanho do padrão) para entrada na tabela de ocorrências para o último caractere no padrão. Portanto, a tabela de ocorrências é somente calculada para os m-1 caracteres do padrão. Então temos,

                        d[x] = min {s | s = m (1<=s<m e padrão [m-s] = x)}

e qualquer caractere que não pertence ao padrão receberá valor m.

            Perceba que d só depende do padrão, ou seja, pode ser pré-calculado antes de se realizar a busca.

 

Exemplo

 

Alfabeto (a) =   A C G T

Tabela de ocorrências:   d[A] = 4

                                    d[C] = 3

                                    d[G] = 1

                                    d[T] = 2  

Tentativa 1

            Texto:        T A   T G [C] A   C T G A

            Padrão:       A   C T G [A]

Delocamento de d[C] = 3

 

Tentativa 2

            Texto:        T A   T G C A   C [T] G A

            Padrão:                A   C T G [A]

Delocamento de d[T] = 2

 

Tentativa 3

Texto:                     T A   T G C A   C T G [A]

Padrão:                                  A   C T G [A]    MATCH

 

Observe que esse algoritmo é pouco eficiente para alfabetos muito pequenos se comparado ao tamanho do padrão, porque o deslocamento será muito pequeno.

 

 

Algoritmo Wu e Manber (WM)

 

            O algoritmo de WM permite uma busca aproximada, ou seja, admite a existência de erros (k) no padrão encontrado no texto. A medida mais comum de aproximação (palavra do texto e padrão) é conhecida como distância de edição. Uma palavra P está a uma distância k da palavra Q se podemos transformar P em Q com uma sequência de inserções de simples caracteres em (lugares arbitrários) P, deleções de caracteres simples em P ou substituições de caracteres. O algoritmo de WM funciona da seguinte forma:

Casamento Exato-

Considere R um vetor de bits de tamanho m (tamanho do padrão).

Considere Rj o valor do vetor R depois do processamento do caractere j do texto. Portanto este vetor terá informações sobre todos os matches dos prefixos de P que finalizam na posição j. Mais precisamente, Rj[i] = 1 se os primeiros i caracteres do padrão casam exatamente os últimos i caracteres até a posição j no texto, ou seja, P1P2...i = Tj-i+1 + Tj-i+2 ... Tj.

            Para cada i com Rj[i] = 1, temos que checar se Tj+1 é igual a Pi+1. Se Rj[i] = 0, então não existe casamento até i e portanto não teremos casamento até i+1. Se Tj+1 = P1 então Rj+1[1] = 1. Se Rj+1[m] = 1 então temos um casamento completo iniciando em (j+1)-m +1 = j-m+2. A transição de Rj para Rj+1 pode ser calculada rapidamente pelo algoritmo de Baeza-Yates and Gonnet. O algoritmo é o seguinte, considere o alfabeto S1, S2,... Sc, para cada caractere Si do alfabeto, pertencente ao padrão, é construído um vetor S de bits de tamanho m sendo que Si[r] = 1 se Pr = Si. É fácil verificar que a transição de Rj para Rj+1 executa não mais que um deslocamento para direita de Rj e uma operação AND com Si, onde Si = Tj+1. Portanto cada execução pode ser executada com somente três simples operações aritméticas (três porque estamos usando C e seu deslocamento não preenche os espaços com um, característica necessária para este algoritmo, e precisaremos efetuar uma operação OR com uma máscara de 1s). Observe o exemplo na tabela 1 abaixo, onde o padrão é aabac e o texto é aabaacaabacab. A tabela 2 mostra as máscaras para a, b e c.

 

            Rj                                                   Si

P          a a b a a c a a b a c a b                a    b    c

a          1 1 0 1 1 0 1 1 0 1 0 1 0                1    0    0

a          0 1 0 0 1 0 0 1 0 0 0 0 0                1    0    0   

b          0 0 1 0 0 0 0 0 1 0 0 0 0                0    1    0

a          0 0 0 1 0 0 0 0 0 1 0 0 0                1    0    0

c          0 0 0 0 0 0 0 0 0 0 1 0 0                0    0    1

Tabela 1                                                     Tabela 2

Casamento Aproximado-

Aqui é possível considerar a existência de erros. Os erros podem ser dos tipos: inserção, deleção ou substituição.

Considere um outro vetor R1j, que indica todos os possíveis casamentos até Tj  com no máximo 1 inserção, ou seja, R1j[i] = 1 se os primeiros i caracteres do padrão casam com i dos últimos i+1 caracteres, até j, do texto. Temos então R[m]=R0[m] = 1 indicando que existe um casamento exato e R1j [m] = 1 indicando que existe um casamento com no máximo uma inserção.

A transição de R0 é realizada da mesma maneira que mostrada anteriormente para casamento exato. Precisamos só definir como será a transição de R1j para R1j+1. Vamos considerar o caso genérico com k erros, então teremos k adicionais vetores R (R1, R2, ... Rk). Onde Rd armazena todos os possíveis casamentos com até d erros. Precisamos definir como seria a transição de Rdj para Rdj+1. Existem quatro possibilidades de se obter um casamento dos i primeiros caracteres com erro <=d até Tj+1:

- Existe um casamento dos primeiros i-1 caracteres com erro <= d até Tj e Tj+1 = Pi. Este caso corresponde ao casamento Tj+1.

- Existe um casamento dos primeiros i-1 caracteres com erro <= d-1

 até Tj. Este caso corresponde a uma substituição de Tj+1.

- Existe um casamento dos primeiros i-1 caracteres com erro <= d-1 até Tj+1. Este caso corresponde a uma deleção de Pi.

- Existe um casamento dos primeiros i caracteres com erro <= d-1 até Tj. Este caso corresponde a uma inserção de Tj+1.

 

Assuma Tj+1=Si. Teremos a seguinte expressão para Rdj+1:

 

Rd0 = 111...10...000 (d elementos 1s)

Rdj+1   = Rshift[Rdj] AND Si OR

               Rshift[Rd-1j] OR

               Rshift[Rd-1j+1] OR Rd-1j

            = Rshift[Rdj] AND Si OR

   Rshift[Rd-1j OR Rd-1j+1] OR

   Rd-1j.

 

Temos 2 deslocamentos, 1 AND e 3 ORs para cada Rd.

Toda busca pode ser modelada usando autômatos. Toda essa teoria anteriormente mostrada usando registradores Rs pode ser melhor visualizada abaixo.

 

A fórmula para qualquer erro mostrada acima pode ser mapeada para o seguinte formato:

(quando d=1)

 

 

 

 

 

 

 

 


(quando d=2)

 

 

 

 

 

 

 

 

 

 

 

 

 


...

 

Ilustraremos abaixo um percurso até um casamento com o erro=1 e padrão=ABCA :

 

Erro=Inserção

MATCH = ABXCA

 

 

 

 

 

 

 

 


Erro=Substituição

MATCH = ABXA

 

 

 

 

 

 

 

 


Erro=Remoção

MATCH = BCA

 

 

 

 

 

 

 

 


Exemplo de busca aproximada permitindo 1 erro (remoção):

 

Padrão = TEXT

Texto = EXTEXT

Mácaras à      M[T] = 1001

                        M[E] = 0100

                        M[X] = 0010

 

Fómulaà          Roj+1 = (R0j>>1 & M[T[i]])     

 

Texto    R0j>>1             Roj+1               R0j+1>>1          R1>>1              R1j+1

                                    0000                                                                 1000

E          1000                 0000                 1000                 1100                 1100

X          1000                 0000                 1000                 1110                 1010    

T          1000                 1000                 1100                 1101                 110[1]ext

E          1100                 0100                 1010                 1110                 1110

X          1010                 0010                 1001                 1111                 101[1]tex

T          1001                 100[1]text         1100                 1101                 110[1]ext

 

                        Casamento                                                        Casamentos

Exato                                                                com erro

 

As colunas onde estão sendo mostrados os casamentos são exatamente a representação binária do acionamento dos estados de um autômato. Por isso quando temos o último bit igual a 1 é sinal de que alcançamos o estado final de um autômato, ou seja, já passamos por todos os estados anteriores admitindo k erros. Isto significa um casamento.

 

2. Análise de complexidade dos principais algoritmos implementados

 

Foi considerada somente as partes obviamente mais pesadas (que levam mais tempo de processamento) do programa. Foi considerada como O(1) qualquer instrução de atribuição ou semelhante. n é o tamanho do texto, m é do padrão e c do alfabeto. A listagem completa dos algoritmos implementados em C e Pascal encontra-se no Anexo1.

 

Boyer-Moore-Horspool (BMH)

Linguagem C

 

long bmh( pd, tx, n )

/* (numero de casamentos) bmh (padrao, linha do texto, tamanho da linha) */

 

   char *pd, *tx;

   int n;

{

  int i, j, k, m, d[MAX_ALPHABET_SIZE]; /* tabela de ocorrencias */

  long match; /* numero de casamentos */

 

1     m = strlen(pd);

     if( m==0 ) return(1);

2     for( k=0; k<MAX_ALPHABET_SIZE; k++ )

                        d[k] = m; /* iniciando tabela de ocorrencias com m */

3     for( k=0; k<m-1; k++ ) d[pd[k]] = m-k-1;   /* atribuindo deslocamento adequado para os caracteres do padrao*/

 

5     for( k=m-1; k < n; k += d[tx[k] & (MAX_ALPHABET_SIZE-1)] ) { /* efetua o deslocamento */

       4   for( j=m-1, i=k; j>=0 && tx[i] == pd[j]; j-- ) i--;  /* checando casamento */

          if( j == (-1) )      /*se houve casamento*/

                        if(opc==5){

                           printf("MATCH posicao %d\n",k-m+1);

                           match++;

                        }else{

                           return(1);  /* retorna 1 quando encontra o primeiro casamento */

                        }

 5.   }

     return(match); /* retorna o numero de casamentos ocorridos nesta linha do texto */

}

 

Bloco 1: Executa m = strlen(pd). A função strlen foi considerada como sendo O(tamanho de pd), neste caso, O(m).

Bloco 2: Executa uma atribuição (d[k]=m, O(1)) c vezes, sendo O(c) (c=tamanho do alfabeto).

Bloco 3: Executa uma atribuição (d[pd[k]]=m-k-1, O(1)) m-1 vezes, sendo O(m-1).

Bloco 4: Executa uma atribuição e uma comparação (O(1) cada) m vezes no pior caso, sendo O(m).

Bloco 5: Executa n-m vezes no pior caso, sendo O(n-m). E mais o bloco 4, sendo O(n-m).O(m) = O(n.m - m**2).

____________________________________________________

O(c) + O(m) + O(n.m - m**2) = O(n.m)

 

Pior caso: O(nm) (tempo de execução do algoritmo de força bruta)

            O pior caso no algoritmo BMH acontece quando temos um erro somente na m-ésima comparação e caractere do bad caracter implica num deslocamento de 1. Se esse caso se repete durante todo o texto então o algoritmo irá realizar:

            n+(n-1)+(n-2)+...+(n-(m-1))

= n.m - (1+2+...(m-1)) = n.m - ((m).(m-1)/2) = n.m - m**2/2 + m/2

 

Exemplo: G será sempre o bad caracter causando um deslocamento de 1 (d[G]=1).

 

Texto:                  G G G G G G G G G G G G

Padrão:               .  .  .  .  .  .  .  .  . C G G

           

Melhor caso: O(n/m)

            O melhor caso ocorre quando o erro ocorre na primeira comparação e o bad character não existe no padrão. Neste caso incrementa-se o deslocamento d de m. Se o melhor caso ocorre repetidamente, o algoritmo examina somente uma fração de 1/m dos caracteres do texto.

 

Caso Médio:

O( n/m ) p/ c muito grande, e

n ( 1/m + 1/2c ) p/  (c<<n),

onde c = tamanho do alfabeto.

 

 

Algoritmo Wu e Manber (WM)

Linguagem C

 

//pré-procecssamento

void preproc(char *p, long k) {

  int i, j, m; /* pattern size */

 

1  m = strlen(p);

2  for (i = 0; i < (k + 1); i++) {   

     Start[i] = 0;  /* zerando a mascara de inicializacao dos Rs */

     for (j = 0; j <= i; j++) {

       Start[i] |= 1 << (m - j);        /* construindo os automatos de forma a permitir um numero de erros <=k*/

    }

2.}

3 for (i = 0; i < ALPHABET_SIZE; i++) {

    S[i] = 0;                    /* zerando a mascara de caracteres do alfabeto */

    valid[i] = ((i >= 'a') && (i <= 'z')) || ((i >= 'A') && (i <= 'Z'));

   /* registrando os caracteres que fazem parte do alfabeto usado*/

3. }

  M = 0;

4 for (i = 0; i < m; i++) {            /* inicializa mascaras de caracteres do alfabeto */

    if (((p[i] >= 'a') && (p[i] <= 'z')) || ((p[i] >= 'A') && (p[i] <= 'Z'))) {

     /* se o caractere do padrao faz parte do alfabeto usado */

      S[p[i]] |= 1 << (m - 1 - i);     /* marca com 1 todas as ocorrencias de p[i] no padrao */

      M |= 1 << (m - 1 - i);           /* marca com 1 todas as posições dos caracteres do padrao para busca exata*/

    }

    else {

      S[' '] |= 1 << (m - 1 - i);       /* S contem 1 em todas as posições cujo caracter nao faz parte do alfabeto*/

    }

4.}

5  for (i = 0; i < ALPHABET_SIZE; i++) {

    if (!valid[i]) {                        

      S[i] = S[' '];             /* caso i nao faz parte do alfabeto recebe a mascara constuida em S[' '] */

    }

5.}

}

 

Bloco 1: Executa m = strlen(p). A função strlen foi considerada como sendo O(tamanho de P), neste caso, O(m).

Bloco 2: Executa S[i:0..k]S[j:0..i] O(1) = S[i:0..k](i+1) = S[j:0..k]i+ S[j:0..k]1=k+(k(k+1)/2)=k**2/2 + 3k/2. Sendo O(k**2).

Bloco 3: O(c)  (c= tamanho do alfabeto)

Bloco 4: O(m)

Bloco 5: O(c)

____________________________________________________

O(c)+O(m)+O(k**2) = O(c) (para c grande em relação a m)

 

Observe que se k é grande de maneira que k**2 ultrapasse c e m, teríamos O(k**2). Isso normalmente não acontece para padrões de tamanho pequeno, onde um número de erros muito grande tornaria a consulta por um padrão quase indefinido. Outro caso poderia ser O(m), se o padrão é maior que c e maior que k**2. Por exemplo, a busca de uma cadeia de m=100 caracteres que representa um gene (c=4) pode tranqüilamente admitir 7 erros (k**2=49), sem perder a qualidade da resposta. Mas, na maioria dos casos, é O(c) para a fase de pré-processamento.

 

//pesquisa

long Wu_Manber(char *p, char *t, long k) {

  long i,

       j,

       m,                        /* tamanho do padrao */

       n,                        /* tamanho da sequencia */

       matchfound,               /* diz se um casamento foi encontrado */

       R0[MAX_NUMBER_OF_ERRORS], /* mascaras de estado anterior */

       R1[MAX_NUMBER_OF_ERRORS]; /* mascaras de estado atual */

  char newt[MAX_MODIFIED_LINE_SIZE]; /* sequencia entre espacos */

  long count;                        /* registra a quantidade de matches ocorridos */

 

  strcpy(newt, " ");

1 strcat(newt, t);

  strcat(newt, " ");

2 n = strlen(newt);

3 m = strlen(p);

  matchfound = FALSE;

4 for (i = 0; i <= k; i++) { /* inicializa mascaras de estado */

    R1[i] = Start[i];                                

    R0[i] = Start[i];

  }

  count=0;

7 for (i = 0; i < n; i++) { /* executa o algoritmo */

    R1[0] = ((R0[0] >> 1) & S[newt[i]]) | (Start[0] & R0[0]);      

/*checando casamento inteiro */

5   for (j = 1; j <= k; j++) {                                                                    

/*checando casamento com erro */

      R1[j] = ((R0[j] >> 1) & S[newt[i]]) |

      ((R0[j-1] | ((R1[j-1] | R0[j-1]) >> 1)) & M) | (Start[0] & R0[j]);

    }

6  for (j = 0; j <= k; j++) { /* atribui estado atual no anterior */

      R0[j] = R1[j];

      if (R1[j] & 1) { /* se bit menos significativo estiver ativo, entao

                          tem-se um casamento na linha atual do texto */

        matchfound = TRUE;

        count+=1;

      }

    }

  }

  return count;

7.}

 

Bloco 1: O(n)  (n=tamanho do texto)

Bloco 2: O(n)

Bloco 3: O(m)

Bloco 4: O(k+1)

Bloco 5: O(k)

Bloco 6: O(k+1)

Bloco 7: O(n)*(O(k)+O(k+1))=O(n.(k+1))

____________________________________________________

O(m)+O(n.(k+1)) = O(n.k) (pior caso)

 

Implementações atuais deste algoritmo executa em O(n) para padrões considerados pequenos.

 

Obs.: As ordens apresentadas acima é uma junção de todas as instruções do bloco, que normalmente são O(1).

            A seguir a análise do mesmo algoritmo na linguagem Pascal (código atualizado – Anexo1).

 

Algoritmo Wu e Manber (WM)

Linguagem Pascal

 

procedure shiftAndPreprocess;

var j, bit, a:  integer;

 

begin

1  m := length(p);

2  for j:=ord('a') to ord('z') do

       Masc[j] := 0;

3  for j:=ord('A') to ord('Z') do

       Masc[j] := 0;

  bit := 1;

4  for j:=1 to m do begin

      a := ord(p[j]);

      Masc[a] := Masc[a] or bit;

      bit := bit shl 1;

  end;

end;

 

 

Bloco 1: O(m)

Bloco 2: O(c) (c = tamanho do alfabeto)

Bloco 3: O(c)

Bloco 4: O(m)

____________________________________________________

O(m)+O(c) = O(c)

 

procedure shiftAndSearch;

var

    i, j :                                       integer;

    R :                                        array[0..m]of integer;

    Rant, Rnovo, bit , bitaux:        integer;

 

begin

1  for j:=1 to m do

       R[j] := 0; 

   

2  n := length(T); 

  bit := 0;

  bitaux:= 1;

 

3  for j:=1 to k do begin

       R[j] := bit;

       bit := (bit shl 1)or bitaux;

       bitaux := bitaux or bit;

  end;

 

  bit := 0;

 4 for i:=1 to n do begin

      Rant := bit;

      Rnovo := ((Rant shl 1) or 1) and Masc[ord(T[i])];

      bit := Rnovo;

 5   for j:=1 to k do begin

 6       Rnovo := ((R[j] shl 1) and Masc[ord(T[i])])or Rant or ((Rant or Rnovo) shl 1);

          Rant := R[j];

          R[j] := Rnovo;

 5.   end;

 7    if ((Rnovo and lastbit)<>0) then begin

          matchs := matchs + 1;

     end;

 4. end;

 

end;

 

Bloco 1: O(m)

Bloco 2: O(n)

Bloco 3: O(k)

Bloco 4: O(n).(O(Bloco 5).O(Bloco 6)+O(Bloco 7))

Bloco 5: O(k).O(Bloco 6)

Bloco 6: O(x) (não sabemos ao certo como são computadas as operações de shift, or e and em Pascal, x = ?)

Bloco 7: O(x)

Bloco 4: O(n).(O(k).O(x)+O(x))

____________________________________________________

O(n).O(k.x) = O(n.k.x) (pior caso)

 

Tentaremos provar a existência de um x1 na seção de testes, através da comparação com o algoritmo em linguagem c. Isso pode ser feito porque temos uma complexidade semelhante e sabemos que as operações em questão na linguagem c executam em O(1).

3. Resultados de experimentos para avaliar empiricamente o desempenho dos algoritmos

 

            Foram utilizados os arquivos de documentos da coleção TREC disponível em /export/texto2/wsj da máquina turmalina.dcc.ufmg.br, com as seguintes características:

 

Coleção            Tamanho (MB)

wsj88                109      

wsj88_10          10        

wsj88_20          20        

wsj89                2.8       

 

A especificação do sistema encontrada neste trabalho apresenta várias carências para que o sistema se torne útil e competitivo na vida real, são elas:

Interface de interação mais poderosa

- Permitir pesquisa orientada a registros (como o agrep) e não somente a linha.

- Tornar mais flexível, permitindo o uso de partes adicionais ao padrão, tipo consulta com definições de intervalos (1-4), conjuntos {x,y,z}, caracteres coringas (?), etc;

- Permitir que o usuário possa especificar que partes do padrão deveriam casar exatamente e que partes poderiam ter erros. E mais, poder fornecer custos para cada tipo de erros de maneira a permitir que tipo de erro mais o interessa;

- Fornecer um help mais detalhado para suporte ao usuário;

- Desenvolver uma interface gráfica.

 

Algoritmo

- Melhorar a eficiência do algoritmo pesquisando e otimizando os que estão sendo atualmente utilizados nas ferramentas de busca mais rápidas, como o agrep.

- Agregar vários programas de busca com um algoritmo capaz de identificar através da entrada do usuário qual seria o programa mais adequado para executar essa busca. Isso permite aproveitar as vantagens de todos os algoritmos com o baixo custo de analisar a entrada do usuário. Cada programa seria voltado para um tipo de problema somente (baseado no tamanho das variáveis de entrada), podendo ser muito mais eficiente, visto que soluções genéricas são sempre mais complexas e sua abrangência limita sua eficiência.

 

4. Testes

 

Nossa implementação permite procurar por vários padrões ao mesmo tempo. Consideramos as seguintes consultas que foram solicitadas para este trabalho:

 

Consulta 1         time ./wm 0 4 wsj89 DCC UFMG dollar

Consulta 2         time ./wm 0 4 wsj89 dia branco

Consulta 3         time ./wm 0 4 wsj89 Macunaima administration

Consulta 4         time ./wm 0 4 wsj89 Brazilian coffee

Consulta 5         time ./wm 0 4 wsj89 New York Stock Exchange

Consulta 6         time ./wm 0 4 wsj89 Manacapuru

Consulta 7         time ./wm 0 4 wsj89 Canada Treasury

Consulta 8         time ./wm 0 4 wsj89 Michael Gregory

Consulta 9         time ./wm 0 4 wsj89 price index

 

Consulta 1         time ./wmpas 0 wsj89 DCC UFMG dollar

Consulta 9

Consulta 1         time agrep -c DCC,UFMG,dollar wsj89

... Consulta 9

Consulta 1         time ./bmh 4 wsj89 DCC UFMG dollar

... Consulta 9

 

As consultas acima fazem parte de scripts usados durante a sessão de testes. Correspondem a consultas com 0 erros e usando a opção 4 que mostra somente o número de linhas com casamentos.

A seguir mostraremos os gráficos obtidos com estas consultas.

 


bmh

wmpas

wm

agrep

11.760

22.890

12.430

0.7

8.130

15.490

8.250

0.71

7.780

15.410

8.310

0.69

7.980

15.340

8.410

0.61

16.240

30.620

16.510

0.65

3.910

7.650

4.180

0.13

7.960

15.210

8.430

0.65

7.900

15.170

8.350

0.65

8.070

15.150

8.250

0.57

 

 

 

                                                                                    Gráfico 1: Comparação entre os desempenhos.

 

            Mesmo admitindo k>1 essa relação permanece. Avaliamos porque nos próximos gráficos.

 

Estudo Empírico x Análise de Complexidade

 

 

Observando a complexidade obtida na análise das nossas implementações, podemos perceber que o algoritmo BMH exige mais tempo quando aumentamos o tamanho do padrão. Esse fato deveria ser imperceptível para o WM, pois WM deveria ser sensível somente ao tamanho do erro. Isso nos motivou a testar separadamente a variação de cada um destes parametros. Obtemos os seguintes gráficos:

 


ERRO

 

 

erro=0,2,4,8  t=109MB  m=14

Agrep

WM

WMPAS

0.09

4.180

8.010

1.040

8.960

15.580

4.780

12.950

24.180

11.910

23.370

42.280

 

 

 

 

                                                                        Gráfico 2: Variando somente o número de erros.

 

O gráfico 2 apresenta a queda de desempenho dos algoritmos com o aumento no número de erros. Esta queda está ligada, também, com o aumento no número de casamentos que aumenta junto com o número  k de erros, isso somente para o agrep que é sensível ao quanto que casa o padrão (“m”), pois usa uma variação do BMH.

 

à WM = O(n.K)

 


TEXTO

 

 

 

t=2.8 10 20 109MB err=0 m=5

 

Agrep

BMH

WM

WMPAS

0

0.1

0.1

0.21

0

0.36

0.39

0.67

0

0.71

0.73

1.380

0

4.040

4.040

7.480

 

 

 

 

 

 

                                                                        Gráfico 3: Variando somente o tamanho do texto.

 

O gráfico 3 ilustra o aumento do  tempo gasto para a pesquisa, com o aumento no tamanho do texto. O tempo também varia com o número de casamentos para os sensíveis ao tamanho do padrão como o BMH e, consequentemente, o agrep. Neste caso (diferente do anterior), não necessariamente será um aumento diretamente proporcional ao tamanho do texto (tamanho do erro). Ou seja, poderíamos ter um aumento no texto e, por variar seu conteúdo, uma redução no número de casamentos. Isso poderia compensar o tempo  do BMH (agrep) para arquivos maiores. No nosso gráfico tomamos o cuidado de ter um número de casamentos quase igual para todos os tamanhos de arquivos usados, de forma que isso seja uma constante e não altere a curva de crescimento.

 

à WM = O(N.k)

à BMH = O(N.m)

 

No próximo teste precisamos variar o tamanho do padrão, consequentemente iremos variar o número de casamentos para um mesmo algoritmo. Ou seja, padrão diferente implica em número de casamentos diferente. Para esse caso pegamos a seguite amostra que não nos deu bons resultados:

 


m=3,5,10 t=109MB erro=0

 

 

Agrep

BMH

WM

WMPAS

0.21

4.340

4.350

7.690

0.17

4.110

4.420

7.740

0.11

4.030

4.290

8.030

 

 

 

 

 

 

 

                                                                        Gráfico 4: Variando somente o tamanho do padrão.

 

O gráfico 4 não mostra consideráveis alterações com o tamanho do padrão. Isso aconteceu porque o  número de casamentos diminuiu com o aumento no tamanho do padrão. Com esse comportamente quase que inversamente proporcional acabamos gerando um gráfico que parece constante no tempo. Isso para BMH (agrep) pois o WM é insensível, realmente, ao tamanho do padrão.

 

Para melhor análise pegamos um texto de tamanho 110MB composto somente de caracteres “x” e procuramos os padrões p=yxx (m=3), p=yxxxxx (m=6) e p=yxxxxxxxxxxx (m=12). Só assim, atingimos o pior caso obtido na análise de complexidade e podemos perceber no gráfico como WM não é sensível à variação do padrão /número de casamentos, por adotar um esquema de paralelismo de bits..

 


m=3 5 10 109MB erro=0

 

agrep

bmh

wm

Wmpas

2.110

5.000

3.360

6.830

3.430

6.810

3.500

6.610

8.200

13.500

3.660

6.670

 

 

 

 

 

 

 

                                                                        Gráfico 4: Analisando o pior caso.

 

            Observe que o que mostramos acima é um caso onde não há casamentos de fato, mas a string é comparada o número máximo de vezes até se perceber um erro.

 

            Observe também que, como o agrep é uma combinação de variantes destes algoritmos [WM 2]. Então ele é sensível a todos os parametros (n, k e m).  E acompanha as mesmas alterações dos algoritmo sensível no momento.

 

            A versão em Pascal do WM apresentou um desempenho inferior aos resultados obtidos com a versão em C. Nossos gráficos mostram um aumento não linear no valor de x da complexidade obtida.

 

à WM = O(n.k)

àWMPAS = O(n.k.X)

 

Apresentamos a seguir gráficos comparando, diretamente, algumas operações lógicas das linguagens.

 

 

 

 

or_c

0

0.04

0.38

3.780

37.980

or_pascal

0

0.07

0.7

6.720

67.240

 

 

 

 

 

 

 

 

 

                                                                                                Gráfico 5: Operação Ou.

 

 

 

 

 

 

 


shift_c

0.01

0.1

0.9

8.810

88.960

shift_pascal

0.03

0.35

3.440

34.110

341.650

 

 

 

 

 

 

 

 

                                                Gráfico 6: Operação de deslocamento.

           

 

Conclusão

 

            O Agrep apresentou melhor desempenho em todos os casos porque usa o melhor de cada algoritmo, por exemplo, para casamento exato usa uma variação do BMH. Estudamos a sensibilidade de cada algoritmo.

            A versão em Pascal do WM apresentou o pior desempenho como era de se esperar segundo [Ziviani]. Isso nos levou a medir isoladamente o custo das operações lógicas em Pascal e compará-las diretamente com as memas operações em C confirmando [Ziviani].

 

 

 

 

 

 

 

BIBLIOGRAFIA

 

[WM 1] Wu, Sun e Manber, Udi. Fast Searching Allowing Errors. Department of Computer Science. University of Arizona.

 

[Baeza] Baeza-Yates, R.; Gonnet, Gaston H. Handbook of Algorithms and Data Structures. Dept. of Computer Science, Univ. of Chile.

 

[Ziviani] N. Ziviani. Processamento de Cadeia de Caracteres. Dept. de Ciência da Computação, Univ. Federal de Minas Gerais, Brasil, 2003.

 

[Charras] Christian Charras, Thierry Lecroq. Handbook of Exact String-Matching Algorithms. Université de Rouen Faculté des Sciences et des Techniques, France.

 

[WM 2] Wu, Sun e Manber, Udi. AGREP - A Fast Approximate Pattern-Matching Tool. Department of Computer Science. University of Arizona.

 

 

 

 

 

ANEXO 1

 

Listagem completa  dos programas implementados e principais scripts utilizados nos testes.

 

            Abaixo podemos visualizar o formato do comando de linha para cada programa:

wm = Shift-And com casamento aproximado em C

wm = Shift-And com casamento aproximado em Pascal

bmh = BMH (casamento exato)

 

Fomato do comando de linha:

./wm erro opcao texto padrao_1 padrao_2 ...padrao_n

            ./bmh opcao texto padrao_1 padrao_2 ...padrao_n

./wmpas erro texto padrao_1 padrao_2 ...padrao_n

 

Erro:

            o numero de erros permitido

Padrao:

            String sendo procurada no texto

Texto:

            Arquivo texto onde buscamos o padrao

 

As opções disponíveis nos algoritmos são:

 

1 quantidade total de casamentos no texto inteiro

2 exibe o numero das linhas com matches "inteiros" e as linhas

3 exibe o numero das linhas com matches e as linhas

4 quantidade de linhas que possuem pelo menos uma ocorrência do padrao

5 mostra a posição exata na linha onde ocorre o padrao

 

Obs.:    A opção 4 foi a única utilizada na fase de testes, comparada a similar – c do grep/agrep.

A opção 2 de casamento inteiro procura o padrão entre espaços. Ocasiões onde o padrão aparece no começo ou final da linha, acompanhado com sinais de pontuação quaisquer ou situações similares, essa ocorrência não será registrada.

           

 

Boyer-Moore-Horspool (BMH)

Linguagem C

 

#include <stdio.h>

#define MAX_ALPHABET_SIZE     256

#define MAX_SIZE                  512

#define MAX_FILE_NAME_SIZE    256

#define MAX_LINE_SIZE         4096

#define MAX_PATTERN_SIZE      29 /* considerando uma palavra de 32 bits */

 

int opc;                          /* opcao de exibicao no terminal de video*/

 

long bmh( pd, tx, n )

/* (numero de casamentos) bmh (padrao, linha do texto, tamanho da linha) */

  

   char *pd, *tx;

   int n;

{

  int i, j, k, m,

            d[MAX_ALPHABET_SIZE];   /* tabela de ocorrencias */

  long match;                             /* numero de casamentos */

 

     m = strlen(pd);

     if( m==0 ) return(1);

     for( k=0; k<MAX_ALPHABET_SIZE; k++ )

                        d[k] = m; /* iniciando tabela de ocorrencias com m */

     for( k=0; k<m-1; k++ ) d[pd[k]] = m-k-1; 

/* atribuindo deslocamento adequado para os caracteres do padrao*/

 

     for( k=m-1; k < n; k += d[tx[k] & (MAX_ALPHABET_SIZE-1)] ) {

/* efetua o deslocamento */

          for( j=m-1, i=k; j>=0 && tx[i] == pd[j]; j-- ) i--; 

/* checando casamento */

          if( j == (-1) )      /*se houve casamento*/

                        if((opc==5)||(opc==1)){

                         /*  printf("MATCH posicao %d\n",k-m+1);*/

                           match++;

                        }else{

                           return(1);

/* retorna 1 quando encontra o primeiro casamento */

                        }

     }

     return(match); /* retorna o numero de casamentos ocorridos nesta linha do texto */

}

 

int main(int argc, char *argv[]) {

 

  FILE     *t; /* text file id */

  long k,m,n,cont,c,match;

  int  y;

  char     line[MAX_LINE_SIZE],          /* linha do arquivo texto */

           p[MAX_PATTERN_SIZE],          /* padrao a ser pesquisado */

           filename[MAX_FILE_NAME_SIZE]; /* nome do arquivo texto */

           

  if (argc < 4) { /* testa se o numero de argumentos esta correto */

            fprintf(stderr, "Numero incorreto de parametros\n");

            printf(" Fomato do comando de linha: \n");

            printf("     ./bmh opcao texto padrao_1 padrao_2 ...padrao_n \n");

            printf(" Padrao: \n");

            printf("              String sendo procurada no texto\n");

            printf(" Texto: \n");

            printf("  Arquivo texto onde buscamos o padrao \n");

            printf(" Opcao:  \n");

            printf("  1 quantidade total de casamentos no texto inteiro\n");

            printf("              2 exibe o numero das linhas com matches e as linhas \n");

            printf("              3 exibe o numero das linhas com matches 'inteiros' e as linhas \n");

            printf("              4 quantidade de linhas que possuem pelo menos uma ocorrencia do padrao  \n");

            printf("              5 mostra a posição exata na linha onde ocorre o padrao  \n");

            exit(1);

  }

 

  strcpy(filename, argv[2]);

  opc=atoi(argv[1]);

 

for (y=3;y<argc;y++)

{

 

  strcpy(p, argv[y]);                   /* p recebe o padrao propriamente dito */

  switch(opc){

        case 3:                              /* match "inteiro" */

         strcpy(p, " ");        /* p recebe um espaco como primeiro caractere */

         strcat(p, argv[1]);             /* p recebe o padrao propriamente dito */

         strcat(p, " ");        /* p recebe um espaco como ultimo caractere */

         break;

  }

  m = strlen(p);                          /* m armazena o tamanho do padrao*/

 

  t = fopen(filename, "r");

  cont=0;

  c=0;

  while (fgets(line, MAX_LINE_SIZE,t)){

     cont++;

     line[strlen(line)-1]='\0';

     match = bmh (p,line,strlen(line));

     if (match!=0){

      switch(opc){

        case 1:      /* quantidade total de casamentos no texto inteiro*/

          c+=match;

          break;

        case 2:      /* exibe o numero das linhas com matches e as linhas*/

          printf("match na linha %d \n %s\n",cont,line);

          break;

        case 3:      /* exibe o numero das linhas com matches "inteiros" e as linhas*/

          printf("Match inteiro na linha %d \n %s\n",cont,line);

                break;

              case 4:            /* quantidade de linhas que possuem pelo menos uma ocorrência do padrao */

                c++;

                break;          

              case 5:            /* mostra a posição exata na linha onde ocorre o padrao */

          printf("linha %d \n",cont);

                break;          

        default: printf("%s: %s\n", filename, line); /*mostra o nome do arquivo e a linha*/

            }

    }

  }

 

  if ((opc==4)||(opc==1)) /*printf("%d\n",c)*/;

}

 

  close(t);

}

 

 

 

Algoritmo Wu e Manber (WM)

Linguagem C

 

#include <stdio.h>

 

#define TRUE                     1

#define FALSE                    0

#define ALPHABET_SIZE          256

#define MAX_LINE_SIZE          512

#define MAX_MODIFIED_LINE_SIZE 512

#define MAX_PATTERN_SIZE        29

#define MAX_NUMBER_OF_ERRORS    28

#define MAX_FILE_NAME_SIZE     256

 

long S[ALPHABET_SIZE],            /* mascaras dos caracteres do alfabeto*/

     valid[ALPHABET_SIZE],        /* diz se o caractere faz parte de palavras */

     Start[MAX_NUMBER_OF_ERRORS], /* mascaras de inicializacao */

     M;                           /* mascara que diz onde a busca

                                     obrigatoriamente e' exata no padrao*/

 

/* Funcao    : preproc

   Objetivos : preprocessar o padrao, montando as mascaras de

               inicializacao, dos caracteres do alfabeto e a de busca

               exata obrigatoria

   Parametros: o padrao e o numero de erros

   Saida     : nenhuma */

 

void preproc(char *p, long k) {

 

  int i,

      j,

      m; /* pattern size */

 

  m = strlen(p);

  for (i = 0; i < (k + 1); i++) {     

    Start[i] = 0; /* zerando a mascara de inicializacao dos Rs */

    for (j = 0; j <= i; j++) {

      Start[i] |= 1 << (m - j);         /* construindo os automatos de forma a

                                                permitir um numero de erros <=k*/

    }

  }

  for (i = 0; i < ALPHABET_SIZE; i++) {

    S[i] = 0;        /* zerando a mascara de caracteres do alfabeto */

    valid[i] = ((i >= 'a') && (i <= 'z')) || ((i >= 'A') && (i <= 'Z'));

    /* registrando os caracteres que fazem parte do alfabeto usado*/

  }

  M = 0;

  for (i = 0; i < m; i++) { /* inicializa mascaras de caracteres do

                                        alfabeto */

    if (((p[i] >= 'a') && (p[i] <= 'z')) || ((p[i] >= 'A') && (p[i] <='Z'))) {

      S[p[i]] |= 1 << (m - 1 - i);    

/* marca com 1 todas as ocorrencias de p[i] no padrao */

      M |= 1 << (m - 1 - i);          

/* marca com 1 todas as posicoes dos caracteres do padrao para busca

exata*/

    }

    else {

      S[' '] |= 1 << (m - 1 - i);

/* S contem 1 em todas as posicoes cujo caracter nao faz parte do

alfabeto*/

    }

  }

  for (i = 0; i < ALPHABET_SIZE; i++) {

    if (!valid[i]) {                        

      S[i] = S[' '];            

/* caso i nao faz parte do alfabeto recebe a mascara constuida em S[' ']

*/

    }

  }

}

 

/* Funcao    : Wu_Manber

   Objetivos : verifica se um padrao ocorre ou nao numa sequencia, com um

               numero de erros menor ou igual a k

   Parametros: o padrao, a sequencia e o numero de erros

   Saida     : verdadeiro se o padrao ocorre na sequencia ou falso, caso

               contrario */

 

long Wu_Manber(char *p, char *t, long k) {

  long i,

       j,

       m,                        /* tamanho do padrao */

       n,                        /* tamanho da sequencia */

       matchfound,               /* diz se um casamento foi encontrado */

       R0[MAX_NUMBER_OF_ERRORS], /* mascaras de estado anterior */

       R1[MAX_NUMBER_OF_ERRORS]; /* mascaras de estado atual */

  char newt[MAX_MODIFIED_LINE_SIZE]; /* sequencia entre espacos */

  long count;           /* registra a quantidade de matches ocorridos */

 

  strcpy(newt, " ");

  strcat(newt, t);

  strcat(newt, " ");

  n = strlen(newt);

  m = strlen(p);

  matchfound = FALSE;

  for (i = 0; i <= k; i++) { /* inicializa mascaras de estado */

    R1[i] = Start[i];                                

    R0[i] = Start[i];

  }

  count=0;

  for (i = 0; i < n; i++) { /* executa o algoritmo */

    R1[0] = ((R0[0] >> 1) & S[newt[i]]) | (Start[0] & R0[0]);      

/*checando casamento inteiro */

    for (j = 1; j <= k; j++) {                                                                     

/*checando casamento com erro */

      R1[j] = ((R0[j] >> 1) & S[newt[i]]) | ((R0[j-1] | ((R1[j-1] | R0[j-1]) >> 1)) & M) | (Start[0] & R0[j]);

    }

    for (j = 0; j <= k; j++) { /* atribui estado atual no anterior */

      R0[j] = R1[j];

      if (R1[j] & 1) { /* se bit menos significativo estiver ativo, entao

                          tem-se um casamento na linha atual do texto */

/*         printf("casou na posicao: %d\n",i);  */

         matchfound = TRUE;

         count+=1;

      }

    }

  }

  return count;

}

 

int main(int argc, char *argv[]) {

 

  FILE *t;                                  /* text file id */

  char line[MAX_LINE_SIZE], /* linha do arquivo texto*/

           p[MAX_PATTERN_SIZE], /* padrao a ser pesquisado*/

           filename[MAX_FILE_NAME_SIZE]; /* nome do arquivo texto*/

  int     opc,                    /* opcao de exibicao o terminal*/

           c,           /*acumulador de ocorrencias do padrao no texto inteiro*/

           cont;                  /* contador de linhas do texto */

  long i,

           j,

           m,                                  /* tamanho do padrao */

           k,                                   /* numero de erros */

           match,                       /* numero de casamentos */          

           y;

  printf("    ");

  if (argc < 5) { /* testa se o numero de argumentos esta correto */

            fprintf(stderr, "Numero incorreto de parametros\n");

            printf(" Fomato do comando de linha: \n");

            printf("     ./wm erro opcao texto padrao_1 padrao_2 ...padrao_n \n");

            printf(" Erro:  \n");

             printf("              o numero de erros permitido \n");

            printf(" Padrao: \n");

            printf("              String sendo procurada no texto\n");

            printf(" Texto: \n");

            printf("  Arquivo texto onde buscamos o padrao \n");

            printf(" Opcao:  \n");

            printf("  1 quantidade total de casamentos no texto inteiro\n");

            printf("              2 exibe o numero das linhas com matches e aslinhas \n");

            printf("              3 exibe o numero das linhas com matches 'inteiros'e as linhas \n");

            printf("              4 quantidade de linhas que possuem pelo menos umaocorrencia do padrao  \n");

            printf("              5 mostra a posição exata na linha onde ocorre opadrao  \n");       

            exit(1);

  }

 

  k = atoi(argv[1]);

  if (k > MAX_NUMBER_OF_ERRORS) { /* valida o numero de erros da busca */

    fprintf(stderr, "Numero de erros maior que o maximo permitido\n");

    exit(1);

  }

  strcpy(filename, argv[3]);

  opc=atoi(argv[2]);

 

 

for (y=4;y<argc;y++)

{

  strcpy(p, argv[y]); /* p recebe o padrao propriamente dito */

  m = strlen(p); /* m armazena o tamanho do padrao */

  switch(opc){

        case 3: //match inteiro

         strcpy(p, " "); /* p recebe um espaco como primeiro caractere */

         strcat(p, argv[4]); /* p recebe o padrao propriamente dito */

         strcat(p, " "); /* p recebe um espaco como ultimo caractere */

         break;

  }

  if (m > MAX_PATTERN_SIZE - 2) {

     fprintf(stderr, "Tamanho do padrao maior que o maximo permitido\n");

     exit(1);

  }else

  if (m > MAX_PATTERN_SIZE) { /* valida o tamanho do padrao */

     fprintf(stderr, "Tamanho do padrao maior que o maximo permitido\n");

     exit(1);

  }

  if (k >= m) {

    fprintf(stderr, "Numero de erros nao e menor que o tamanho do padrao\n");

    exit(1);

  }

  preproc(p, k);

  t = fopen(filename, "r");

  c=0;

  cont=0; 

 

  while(fgets(line, MAX_LINE_SIZE, t)) {

    line[strlen(line) - 1] = '\0';

    cont+=1;

    if (match=Wu_Manber(p, line, k)) {

      switch(opc){

        case 1: /* quantidade total de casamentos no texto inteiro*/

          c+=match;

          break;

        case 2: /* exibe o numero das linhas com matches e as linhas*/

          printf("match na linha %d \n %s\n",cont,line);

          break;

        case 3: /* exibe o numero das linhas com matches

                        "inteiros" e as linhas*/

          printf("Match inteiro na linha %d \n %s\n",cont,line);

              break;

        case 4:      /* quantidade de linhas que possuem pelo menos uma

                                    ocorrência do padrao */

                c++;

                break;          

        case 5: /* mostra a posição exata na linha onde ocorre o padrao*/

            printf("linha %d \n",cont);

                break;          

        default: printf("%s: %s\n", filename, line);

      }

    }

  }

  if ((opc==1)||(opc==4)) /*printf("%d\n",c)*/;

}

 close(t);

}

 

 

Algoritmo Wu e Manber (WM)

Linguagem Pascal

 

program ShiftAndAprox(output);

 

const

  TAM_TEXTO =   1000000;

  TAM_ALFABETO= 255;

  TAM_PADRAO =  10;

  TAM_REGISTRO= 10;

 

var

  p :                  String[TAM_PADRAO];

  T :                              String[TAM_TEXTO];

  k,r,i :                           integer;

  matchs, lastbit:integer;

  F :                              text;

  Masc:              array[0..TAM_ALFABETO]of integer;

 

 

procedure shiftAndPreprocess (p:String);

var j, bit, a, m:  integer;

 

begin

{pre-processamento}

 

  for j:=ord('a') to ord('z') do

     Masc[j] := 0;

  for j:=ord('A') to ord('Z') do

     Masc[j] := 0;

 

  bit := 1;

  m := length(p);

  for j:=1 to m do begin

    a := ord(p[j]);

    Masc[a] := Masc[a] or bit;

    lastbit := bit;

    bit := bit shl 1;  

  end;

end;

 

procedure shiftAndSearch (var k:integer; var T,p:String);

var m, n:                       integer;

    i, j ,a:            integer;

    R :                            array[0..m]of integer;

    Rant, Rnovo: integer;

    bit , bitaux:   integer;

 

begin

 

{pesquisa}

  for j:=1 to m do

       R[j] := 0;

 

  n := length(T);

  bit := 0;

  bitaux:= 1;

 

  for j:=1 to k do begin

      R[j] := bit;

      bit := (bit shl 1)or bitaux;

      bitaux := bitaux or bit;

  end;

 

  bit := 0;

  for i:=1 to n do begin

    Rant := bit;

    Rnovo := ((Rant shl 1) or 1) and Masc[ord(T[i])];

    bit := Rnovo;

    for j:=1 to k do begin

       Rnovo := ((R[j] shl 1) and Masc[ord(T[i])])or Rant or ((Rant or Rnovo) shl 1);

       Rant := R[j];

       R[j] := Rnovo;

    end;

    if ((Rnovo and lastbit)<>0) then begin

{       Writeln("casou na posicao: ",i);}

       matchs := matchs + 1;

    end;

  end;

 

end;

 

{programa principal}

 

Begin

   if ParamCount < 3 then begin

     writeln("./wmpas erro texto padrao");

     halt(1);

   end;

 

   T := ParamStr(2);

   Val(ParamStr(1),k,r);

   assign(F,T);

 

  for i:=3 to ParamCount do

  begin

      p := ParamStr(i);

      reset(F);

      matchs := 0;  

      shiftAndPreprocess(p);

      while not EOF(F) do

      begin

         readln(F,T);

         shiftAndSearch(k,T,p); 

      End;

      writeln(matchs);

   end;

 

   close(F);

 

end.

 

 

 

 

 

 

 

 

 

 

 

 

TESTES (scripts)

 

1- Variando o tamanho do ERRO.

 

echo Agrep err=0 2 4 8 109MB m=14

time agrep -c administration wsj88

time agrep -c -2 administration wsj88

time agrep -c -4 administration wsj88

time agrep -c -8 administration wsj88

 

echo wm err=0 2 4 8 109MB m=14

time ./wm 0 4 wsj88 administration

time ./wm 2 4 wsj88 administration

time ./wm 4 4 wsj88 administration

time ./wm 8 4 wsj88 administration

 

echo wmpas err=0 2 4 8 109MB m=14

time ./wmpas 0 wsj88 administration

time ./wmpas 2 wsj88 administration

time ./wmpas 4 wsj88 administration

time ./wmpas 8 wsj88 administration

 

2- Variando o tamanho do PADRAO.

 

echo agrep m=3 5 10 109MB err=0

time agrep -c dia wsj88

time agrep -c price wsj88

time agrep -c Manacapuru wsj88

 

echo bmh m=3 5 10 109MB  err=0

time ./bmh 4 wsj88 dia

time ./bmh 4 wsj88 price

time ./bmh 4 wsj88 Manacapuru

 

echo wm m=3 5 10 109MB err=0

time ./wm 0 4 wsj88 dia

time ./wm 0 4 wsj88 price

time ./wm 0 4 wsj88 Manacapuru

 

echo wmpas m=3 5 10 109MB err=0

time ./wmpas 0 wsj88 dia

time ./wmpas 0 wsj88 price

time ./wmpas 0 wsj88 Manacapuru

 

3- Variando o tamanho do TEXTO.

 

echo agrep t=2.8 10 20 109MB err=0 m=5

time agrep -c index wsj89

time agrep -c index wsj88_10

time agrep -c index wsj88_10

time agrep -c index wsj88 

 

echo bmh t=2.8 10 20 109MB err=0

time ./bmh 4 wsj89 index

time ./bmh 4 wsj88_10 index

time ./bmh 4 wsj88_20 index

time ./bmh 4 wsj88 index

 

echo wm t=2.8 10 20 109MB err=0

time ./wm 0 4 wsj89 index

time ./wm 0 4 wsj88_10 index

time ./wm 0 4 wsj88_20 index 

time ./wm 0 4 wsj88 index

 

echo wmpas t=2.8 10 20 109MB err=0  

time ./wmpas 0 wsj89 index

time ./wmpas 0 wsj88_10 index

time ./wmpas 0 wsj88_20 index

time ./wmpas 0 wsj88 index