Bene.
Proseguendo nello studio delle tabelle hash e delle liste concatenate, due cose fondamentali mi servono: una funzione che converta una stringa in un numero, ossia la funzione hash, e poi la creazioni di nodi.
Rivediamo la prima.
Mi serve una funzione per trasformare i caratteri nei loro codici ASCII e poi farne la somma.
Questa l'ho già vista prima ma ora la ripasso.
Come parametro, ci sarà una stringa.
Sub converti(stringa As String)
End Sub
Quindi predispongo una matrice dinamica:
Sub converti(stringa As String)
Dim caratteri() As Char
End Sub
Ridimensiono la matrice di caratteri alla lunghezza della stringa (-1 perché la matrice comincia con 0):
Sub converti(stringa As String)
Dim caratteri() As Char
ReDim caratteri(Len(stringa) - 1)
End Sub
Ora ho una matrice di caratteri di lunghezza uguale a quella della stringa.
Ad ogni elemento della matrice devo far corrispondere un carattere della stringa.
Sub converti(stringa As String)
Dim caratteri() As Char
ReDim caratteri(Len(stringa) - 1)
For n = 0 To Len(stringa) - 1
caratteri(n) = Mid(stringa, n, 1)
Next
End Sub
Come prova, scrivo nella finestra di Debug i singoli valori della matrice caratteri():
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
converti("ciao")
End Sub
Sub converti(stringa As String)
Dim caratteri() As Char
ReDim caratteri(Len(stringa) - 1)
For n = 1 To Len(stringa)
caratteri(n - 1) = Mid(stringa, n, 1)
Next
For n = 0 To UBound(caratteri)
Debug.Print(caratteri(n))
Next
End Sub
e vediamo cosa ne viene fuori, per vedere se abbiamo fatto tutta la procedura corretta:
c
i
a
o
Bene.
Adesso devo calcolare il valore ASCII dei caratteri e sommarli insieme:
Sub converti(stringa As String)
Dim asciValue As Integer = 0
Dim caratteri() As Char
ReDim caratteri(Len(stringa) - 1)
For n = 1 To Len(stringa)
caratteri(n - 1) = Mid(stringa, n, 1)
Next
For n = 0 To UBound(caratteri)
asciValue += Asc(caratteri(n))
Next
Debug.Print(asciValue)
End Sub
Ho predisposto un valore asciValue pari a zero che, ripassando tutti i caratteri della matrice convertiti nel loro valore ASCII vi vengono sommati:
c
i
a
o
412
Ma non si può fare a meno della matrice?
Facciamo un calcolo abbreviato.
Sub converti(stringa As String)
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Debug.Print(Mid(stringa, n, 1))
Next
Debug.Print(asciValue)
End Sub
c
i
a
o
412
Considerando che abbiamo come input una stringa e come output vogliamo sapere il valore della somma dei codici ASCII dei suoi caratteri (possiamo evitare di stampare tutti i caratteri, cosa che ci serviva solo per verificare che avevamo fatto tutto bene), trasformiamo la Sub in una Function:
Function converti(stringa As String) As Integer
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Next
Return asciValue
End Function
E verifichiamola:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.Print(converti("abc"))
End Sub
Function converti(stringa As String) As Integer
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Next
Return asciValue
End Function
End Class
294
...che corrisponde alla somma dei codici ASCII di a,b,c.
Vorrei trovare un nome più consono alla funzione:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Debug.Print(asciiSum("abc"))
End Sub
Function asciiSum(stringa As String) As Integer
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Next
Return asciValue
End Function
End Class
Ora, in previsione dell'uso di liste concatenate come mezzo per superare le collisioni, dovrei inserire questi valori trovati in una serie di "nodi".
Creiamo la classe Nodo.
Class Nodo
Public valore As Integer
Public prossimo As Nodo
End Class
La proprietà
valore è quella che ospita i valori asciiSum, ossia quelli ottenuti con la funzione hash. La proprietà
prossimo dovrebbe indicare il nodo successivamente concatenato nell'eventualità ce ne fosse bisogno.
Come conciliare la funzione che ho trovato prima con i nodi?
Avrei una matrice di nodi, che però non dovrebbero essere istanziati tutti, bensì solo quelli utilizzati.
Ad esempio, per un asciSum di 294 dovrei istanziare l'elemento 294 dell'array di nodi.
Facciamo una lista di 500 nodi, ossia una matrice di 500 variabili nodo (non istanziate), e una variabile che servirà da index ogni volta che si creerà un nuovo valore.
Dim nodeArray(500) As Integer
Dim hashFunction As Integer
Mi sta venendo l'idea di chiamare la funzione, anziché asciValue, hashFunction, a sottolineare la funzione più generale di funzione hash.
Ora creo la funzione che crea un nuovo item:
Sub createItem(stringa As String)
End Sub
Ecco scritto il tutto:
Public Class Form1
Dim nodeArray(366) As Nodo
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
createItem("abc")
createItem("fds")
createItem("ghr")
createItem("uyj")
createItem("jhk")
createItem("fgh")
elenca()
End Sub
Sub createItem(stringa As String)
nodeArray(hashFunction(stringa)) = New Nodo
nodeArray(hashFunction(stringa)).valore = stringa
nodeArray(hashFunction(stringa)).prossimo = Nothing
End Sub
Sub elenca()
For n = 0 To UBound(nodeArray)
If nodeArray(n) Is Nothing Then
Debug.Print(n & "---")
Else
Debug.Print(n & "---" & nodeArray(n).valore)
End If
Next
End Sub
Function hashFunction(stringa As String) As Integer
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Next
Return asciValue
End Function
End Class
Class Nodo
Public valore As String
Public prossimo As Nodo
End Class
Ed ecco l'output:
0---
1---
2---
3---
4---
5---
6---
7---
8---
9---
10---
11---
12---
13---
14---
15---
16---
17---
18---
19---
20---
21---
22---
23---
24---
25---
26---
27---
28---
29---
30---
31---
32---
33---
34---
35---
36---
37---
38---
39---
40---
41---
42---
43---
44---
45---
46---
47---
48---
49---
50---
51---
52---
53---
54---
55---
56---
57---
58---
59---
60---
61---
62---
63---
64---
65---
66---
67---
68---
69---
70---
71---
72---
73---
74---
75---
76---
77---
78---
79---
80---
81---
82---
83---
84---
85---
86---
87---
88---
89---
90---
91---
92---
93---
94---
95---
96---
97---
98---
99---
100---
101---
102---
103---
104---
105---
106---
107---
108---
109---
110---
111---
112---
113---
114---
115---
116---
117---
118---
119---
120---
121---
122---
123---
124---
125---
126---
127---
128---
129---
130---
131---
132---
133---
134---
135---
136---
137---
138---
139---
140---
141---
142---
143---
144---
145---
146---
147---
148---
149---
150---
151---
152---
153---
154---
155---
156---
157---
158---
159---
160---
161---
162---
163---
164---
165---
166---
167---
168---
169---
170---
171---
172---
173---
174---
175---
176---
177---
178---
179---
180---
181---
182---
183---
184---
185---
186---
187---
188---
189---
190---
191---
192---
193---
194---
195---
196---
197---
198---
199---
200---
201---
202---
203---
204---
205---
206---
207---
208---
209---
210---
211---
212---
213---
214---
215---
216---
217---
218---
219---
220---
221---
222---
223---
224---
225---
226---
227---
228---
229---
230---
231---
232---
233---
234---
235---
236---
237---
238---
239---
240---
241---
242---
243---
244---
245---
246---
247---
248---
249---
250---
251---
252---
253---
254---
255---
256---
257---
258---
259---
260---
261---
262---
263---
264---
265---
266---
267---
268---
269---
270---
271---
272---
273---
274---
275---
276---
277---
278---
279---
280---
281---
282---
283---
284---
285---
286---
287---
288---
289---
290---
291---
292---
293---
294---abc
295---
296---
297---
298---
299---
300---
301---
302---
303---
304---
305---
306---
307---
308---
309---fgh
310---
311---
312---
313---
314---
315---
316---
317---jhk
318---
319---
320---
321---ghr
322---
323---
324---
325---
326---
327---
328---
329---
330---
331---
332---
333---
334---
335---
336---
337---
338---
339---
340---
341---
342---
343---
344---uyj
345---
346---
347---
348---
349---
350---
351---
352---
353---
354---
355---
356---
357---
358---
359---
360---
361---
362---
363---
364---
365---
366---
Ecco: le stringhe da porre nella tabella erano queste:
createItem("abc")
createItem("fds")
createItem("ghr")
createItem("uyj")
createItem("jhk")
createItem("fgh")
mentre quelle ritrovate nella tabella sono:
294---abc
309---fgh
317---jhk
321---ghr
344---uyj
c'è una collisione fra le stringhe "fds" e "jhk", la cui somma è sempre 317
Proviamo con il metodo Open Addressing.
Innanzitutto bisogna vedere come individuare una collisione.
Con questo accorgimento, jhk non dovrebbe sostituire fds.
Sub createItem(stringa As String)
If nodeArray(hashFunction(stringa)) Is Nothing Then
nodeArray(hashFunction(stringa)) = New Nodo
nodeArray(hashFunction(stringa)).valore = stringa
nodeArray(hashFunction(stringa)).prossimo = Nothing
End If
End Sub
Vediamo...
294---abc
309---fgh
317---fds
321---ghr
344---uyj
Confrontando con la lista precedente:
294---abc
309---fgh
317---jhk
321---ghr
344---uyj
Esatto! Come nelle previsioni!
Ora, individuata la collisione, dove andiamo a mettere quel jhk che la funzione hash ha reso uguale al fds?
Dobbiamo scorrere avanti nella lista fino a trovare uno "slot" vuoto.
Innanzitutto è più comodo modificare così la funzione createItem:
Sub createItem(stringa As String)
Dim hf As Integer = hashFunction(stringa)
If nodeArray(hf) Is Nothing Then
nodeArray(hf) = New Nodo
nodeArray(hf).valore = stringa
nodeArray(hf).prossimo = Nothing
End If
End Sub
Quindi...
Sub createItem(stringa As String)
Dim hf As Integer = hashFunction(stringa)
If nodeArray(hf) Is Nothing Then
nodeArray(hf) = New Nodo
nodeArray(hf).valore = stringa
nodeArray(hf).prossimo = Nothing
Else
Do
hf = hf + 1
If nodeArray(hf) Is Nothing Then
nodeArray(hf) = New Nodo
nodeArray(hf).valore = stringa
nodeArray(hf).prossimo = Nothing
End If
Loop
End If
End Sub
Aumentiamo il numero di indice della variabile nodo facente parte della matrice nodeArray, fin quando troviamo una variabile non inizializzata, che inizializziamo con il valore rimastoci "vacante".
Proviamo.
Ecco una migliore gestione che evita al ciclo Do... Loop di andare avanti superando i limiti della matrice nodeArray:
Public Class Form1
Dim nodeArray(366) As Nodo
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
createItem("abc")
createItem("fds")
createItem("ghr")
createItem("uyj")
createItem("jhk")
createItem("fgh")
elenca()
End Sub
Sub createItem(stringa As String)
Dim hf As Integer = hashFunction(stringa)
If nodeArray(hf) Is Nothing Then
nodeArray(hf) = New Nodo
nodeArray(hf).valore = stringa
nodeArray(hf).prossimo = Nothing
Else
Do
hf = hf + 1
If hf > UBound(nodeArray) Then Exit Do
If nodeArray(hf) Is Nothing Then
nodeArray(hf) = New Nodo
nodeArray(hf).valore = stringa
nodeArray(hf).prossimo = Nothing
Exit Do
End If
Loop
End If
End Sub
Sub elenca()
For n = 0 To UBound(nodeArray)
If nodeArray(n) Is Nothing Then
Debug.Print(n & "---")
Else
Debug.Print(n & "---" & nodeArray(n).valore)
End If
Next
End Sub
Function hashFunction(stringa As String) As Integer
Dim asciValue As Integer = 0
For n = 1 To Len(stringa)
asciValue += Asc(Mid(stringa, n, 1))
Next
Return asciValue
End Function
End Class
Class Nodo
Public valore As String
Public prossimo As Nodo
End Class
Ed ecco come è stata sistemata la collisione:
294---abc
309---fgh
317---fds
318---jhk
321---ghr
344---uyj
Ecco, ora "jhk" non sparisce più ma viene "incasellato" con l'indice 318, primo "slot" libero dopo quello con cui si contendeva l'indice.
Confronto con le stringhe da "disporre" nel codice:
createItem("abc")
createItem("fds")
createItem("ghr")
createItem("uyj")
createItem("jhk")
createItem("fgh")
Perfetto!
Ora se io voglio ritrovare il punto in cui si trova la stringa "jhk", come faccio?
Tramite la funzione hash (hashFunction) trovo immediatamente la "locazione" 317. A questo punto faccio un confronto che non mi dà la corrispondenza, in quanto in quella "locazione" io ci trovo "fds". Così con un ciclo "while" scorro le locazioni successive fino a quando il confronto darà esito positivo.
Molto più economico che cercare "jhk" su tutta la matrice nodeArray di sana pianta a cominciare dal primo fino a quando avrò trovato la stringa!