• Non ci sono risultati.

๐‘ con rango 19. Il suffisso TAC in #๐‘  $๐‘ค risulta essere preceduto da T quando occorre come

suffisso di #GATTAC โˆˆ ๐ท, il quale ha un rango 0 in ๐ท, e da A quando occorre come suffisso di T!GATACโˆˆ ๐ท, il quale ha un rango 3 in ๐ท. Applicando il Lemma 5.7 e attraverso lโ€™ordinamento lessicografico dei suffissi di ๐‘ƒ = [0,1,3,1,4,2] riportato in Figura 5.2, si ha che il suffisso โ€œ1 3 1 4 2โ€ successivo a โ€œ0โ€ `e lessicograficamente minore al suffisso โ€œ1 4 2โ€ successivo a โ€œ3โ€, quindi T precede A nella BWT di ๐‘ 

'

che alla fine risulta: ATTTTTTCCGGGGAAA!$!AAATATAA.

#GATTACAT!GATACAT!GATTAGATA$$ ACAT!GATACAT!GATTAGATA$$ ACAT!GATTAGATA$$ AGATA$$ T!GATACAT!GATTAGATA$$ T!GATTAGATA$$ 0 1 3 1 4 2 1 3 1 4 2 1 4 2 2 3 1 4 2 4 2

Figura 5.2: Suffissi del parser๐‘ƒ = [0,1,3,1,4,2] ordinati lessicograficamente.

Poichยดe `e necessario solo il dizionario ๐ท e le frequenze dei suoi elementi in ๐‘ƒ per costruire e me- morizzare, attraverso il Lemma 5.6, le sottostringhe della BWT di ๐‘ 

'

composte da tutti i caratteri mappati agli elementi di ๐‘ per cui ๐›ฝ ha prodotto un solo carattere distinto; `e richiesto uno spazio proporzionale alla lunghezza totale degli elementi di ๐ท. Sebbene poi, le sottostringhe dei caratteri mancanti nellโ€™ordine in cui appaiono nella BWT, costruite mediante il Lemma 5.7, possono occu- pare pi`u spazio della combinazione di ๐ท e ๐‘ƒ; man mano che vengono generate possono essere con- catenate alle sequenze del passaggio precedente, utilizzando quindi uno workspace proporzionale alla somma della lunghezza di ๐‘ƒ e gli elementi di ๐ท.

Supponendo quindi di poter riconoscere rapidamente le stringhe in ๐ธ, possiamo calcolare rapi- damente ๐ท e ๐‘ƒ con una scansione su ๐‘ . Attraverso ๐ท e ๐‘ƒ, con i Lemmi 5.6 e 5.7, possiamo calcolare la BWT di ๐‘ 

'

= ๐‘ $ ordinando i suffissi degli elementi di ๐ท e i suffissi di ๐‘ƒ. In quanto ad oggi esisto- no algoritmi in grado di eseguire lโ€™ordinamento in tempo e spazio lineare quando si lavora nella memoria interna, vale il risultato teorico espresso nel Teorema 5.1.

5.2

Implementazione del Prefix-Free Parsing

Fornita la dimensione ๐‘ค della finestra ๐‘Š e selezionato un numero primo ๐‘, lโ€™insieme ๐ธ, descritto nella sezione precedente, viene definito come lโ€™insieme delle stringhe ๐‘’ tali che |๐‘’| = ๐‘ค e per cui ๐ป(๐‘’) mod ๐‘ = 0, dove ๐ป `e la funzione hash denominata come Karp-Rabin fingerprint(17). I para-

๐‘’ ๐‘Š

๐‘ 

๐œ‘

frase precedente frase successiva

Figura 5.3: Rappresentazione dello scorrimento della finestra๐‘Š sulla stringa ๐‘ . La frase attuale ๐œ‘ terminata intercorre tra lโ€™inizio della precedente occorrenza di ๐‘’ โˆˆ ๐ธ e la fine di ๐‘Š .

(17)La fingerprint `e una sequenza alfanumerica, o una stringa di bit, di lunghezza prefissata che identifica un certo file con le

caratteristiche intrinseche stesse del file. La (Karp-)Rabin fingerprint, proposta da Rabin [48] nel 1981, `e una funzione di rolling hash utilizzata come fingerprint e definita tramite polinomi su un campo finito. La Karp-Rabin fingerprint, ad esempio, viene utilizzata per lโ€™organizzazione dei filesystem oppure per il pattern-matching.

CAPITOLO 5. Prefix-Free Parsing 5.2. Implementazione del Prefix-Free Parsing metri ๐‘ค e ๐‘ influenzano la dimensione del dizionario di frasi distinte e il numero di frasi nel parser. Man mano che ๐‘Š scorre sulla stringa ๐‘  (vedi Figura 5.3) la frase attuale, qui indicata con ๐œ‘, corri- sponde alla sottostringa che intercorre tra lโ€™inizio della precedente occorrenza di una stringa ๐‘’ โˆˆ ๐ธ e la fine della stringa di ๐‘Š . I risultati dellโ€™applicazione di ๐ป sulla sottostringa coperta da ๐‘Š (๐ป(๐‘Š )) e sullโ€™intera frase attuale che `e appena stata processata (๐ป(๐œ‘)) vengono memorizzati. Ogni volta che risulta ๐ป(๐‘Š ) = 0 mod ๐‘, la frase attuale termina e la frase successiva inizia dallโ€™inizio della finestra ๐‘Š . Inoltre, si antepone un carattere null (che pu`o essere univoco senza la distinzione fatta nella teoria della sezione precedente) alla prima frase (precedentemente indicato con โ€œ#โ€) e si appendono ๐‘ค copie del carattere null allโ€™ultima frase (ossia la precedente sequenza $๐‘ค).

Lโ€™algoritmo tiene traccia, sia dei risultati della funzione hash calcolati sulle frasi contenute nel dizionario ๐ท attraverso lโ€™insieme ๐ท

'

, che della loro frequenza. Pi`u precisamente, quando la frase attuale ๐œ‘ termina, il risultato della funzione hash ๐ป(๐œ‘) viene appeso alla lista ๐‘ƒ, e viene verificato se ๐ป(๐œ‘) `e presente nel dizionario ๐ท

'

. Se ๐ป(๐œ‘) โˆ‰ ๐ท

'

allora ๐œ‘ viene aggiunta a ๐ท e la sua frequenza impostata a 1; se invece ๐ป(๐œ‘) = ๐ป(๐œ‘

'

) โˆˆ ๐ท

'

viene verifico che le due frasi siano effettivamente le stesse, ๐œ‘ = ๐œ‘

'

โˆˆ ๐ท, e si incrementa la sua frequenza.

Al termine dello scorrimento della finestra ๐‘Š sullโ€™intera stringa ๐‘  e della funzione di parsing, sono stati generati il dizionario ๐ท e il parser ๐‘ƒ = ๐‘1, ๐‘2, โ€ฆ , ๐‘โ„Ž, dove ogni frase ๐‘๐‘–, con 1 โ‰ค ๐‘– โ‰ค โ„Ž, `e

rappresentata dal proprio risultato della funzione hash. Il passaggio successivo consiste nellโ€™ordi- namento lessicografico di ๐ท e nella sostituzione di ciascuna frase ๐‘๐‘–con il rango lessicografico di ๐‘๐‘–

in ๐ท. Cos`ฤฑ facendo si ottiene il parser come una sequenza di interi di 4 byte ciascuno. La procedura sviluppata da Boucher et al. scrive il dizionario sul disco frase dopo frase nellโ€™ordine lessicografico e utilizzando un terminatore alla fine di ciascuna frase, e memorizza, su un file separato, la frequen- za di ciascuna frase con un intero di 4 byte. Lโ€™utilizzo di 4 byte per ogni intero memorizzano non garantisce la migliore compressione possibile del file, ma permette di processare pi`u rapidamente i valori nei passaggi successivi. Infine, per elaborare gli elementi di ๐‘ che sono anche elementi di ๐ท, viene memorizzato un array ๐ด di lunghezza โ„Ž = | ๐‘ƒ | tale che, per 1 โ‰ค ๐‘– โ‰ค โ„Ž, ๐ด[๐‘–] corrisponde al (๐‘ค + 1)-esimo carattere di ๐‘๐‘–a partire dalla fine, ossia ๐ด[๐‘–] = ๐‘๐‘–[| ๐‘๐‘–| โˆ’ (๐‘ค + 1)].

Successivamente viene calcolata la BWT del parser ๐‘ƒ (in cui gli elementi sono interi da 4 byte) attraverso lโ€™algoritmo SACA-K [49], che tra gli algoritmi che operano in tempo lineare, `e quello che utilizza uno workspace minore. Invece di memorizzare la stringa BWT(๐‘ƒ) = ๐ต = ๐‘1, ๐‘2, โ€ฆ , ๐‘โ„Ž, per

ogni frase ๐‘‘๐‘– contenuta nel dizionario ๐ท lessicograficamente ordinato, si memorizza la lista delle

posizioni degli elementi di ๐ต in cui appare ๐‘‘๐‘–; tale lista viene anche denominata inverted-list della

frase ๐‘‘๐‘–(vedere Figura 5.4(a)). S osservi che, la lunghezza della inverted-list di una frase corrisponde

alla sua frequenza, ed `e possibile quindi memorizzare la concatenazione delle inverted-list per un totale di 4โ„Ž byte. Considerata ๐‘

'

๐‘–la frase che precede ๐‘๐‘–in ๐‘ƒ, in questo passaggio vengono permutati

i valori di ๐ด cos`ฤฑ che da adesso ๐ด[๐‘–] = ๐‘

'

๐‘–[| ๐‘

'

๐‘–| โˆ’ (๐‘ค + 1)] (vedere Figura 5.4(b)).

Lโ€™ultima fase dellโ€™algoritmo calcola la BWT di ๐‘ . Anche in questo caso, la pratica si discosta un poโ€™ del Prefix-Free Parsing: invece di ordinare lessicograficamente i suffissi ๐‘ง di ๐ท per cui | ๐‘ง | โ‰ฅ ๐‘ค ven- gono ordinati tutti i suffissi ๐‘ง escludendo successivamente quelli con lunghezza inferiore o uguale a ๐‘ค. Lโ€™algoritmo utilizzato da Boucher et al. nellโ€™implementazione `e gSACAK [43], che calcola il SA e lโ€™array LCP dellโ€™insieme delle frasi contenute nel dizionario ๐ท. Procedendo poi come descritto nella sezione precedente, se durante la scansione dellโ€™insieme ordinato ๐‘ si incontra ๐‘ง