add documentation kmin
All checks were successful
continuous-integration/drone/push Build is passing

- edit getIndex into HashTable
- add test kmin for k equal to 5
This commit is contained in:
Raffaele Mignone 2019-05-04 15:14:26 +02:00
parent 6c1bb2bc60
commit 6a554c55c4
Signed by: norangebit
GPG Key ID: F5255658CB220573
3 changed files with 101 additions and 2 deletions

79
doc/exercises/kmin.md Normal file
View File

@ -0,0 +1,79 @@
---
author: Raffaele Mignone
title: Caratterizzazione della complessità di un algoritmo per la ricerca del k minimo in uno stream
keywords:
- Complessità
- Coda a priorità
- k minimo
subject: Caratterizzazione della complessità
papersize: a4
lang: it-IT
---
# Ricerca del k minimo
## Traccia
Scrivere un programma che, dato uno stream di interi in ingresso, restituisce in ogni momento il k-esimo elemento più piccolo.
## Soluzione
La risoluzione del problema è avvenuta attraverso un cambiamento di prospettiva.
Infatti la ricerca del $k$-esimo elemento più piccolo è equivalente alla ricerca dell'elemento più grande all'interno di una collezione di $k$ elementi.
Una volta strutturato il problema come ricerca del massimo può essere velocemente risolto attraverso una coda a priorità massima come mostrato nel @lst:ignoreInput.
```{#lst:ignoreInput .kotlin caption="Algoritmo di risoluzione non sensibile l'input"}
fun insert(elem: T): Option<T> {
heap.insert(elem)
if (heap.size() > k)
heap.pop()
if (heap.size() < k)
return None
return heap.peek()
}
```
## Caratterizzazione della complessità
La complessità della funzione `insert` del @lst:ignoreInput è legata alla complessità delle funzioni della coda.
Sia la funzione per inserire un elemento nel binary heap che quella per rimuoverlo hanno una complessità logaritmica legata al numero di elementi nella coda.
Dato che una delle condizioni fondamenta per il corretto funzionamento dell'algoritmo è l'avere una coda sempre lunga $k$ possiamo affermare che la complessità della funzione `insert` è pari a $logk$.
Visto che la funzione `insert` sarà invocata per ogni elemento dello stream si ha una complessità totale di $nlogk$.
La soluzione mostrata nel @lst:ignoreInput non è sensibile all'input del problema quindi rappresenta un upper bound che può essere migliorato come mostrato nel @lst:smart.
```{#lst:smart .kotlin caption="Algoritmo di risoluzione sensibile all'input"}
fun insert(elem: T): Option<T> {
heap.peek().fold(
{ heap.insert(elem) },
{
if (elem < it || heap.size() < k)
heap.insert(elem)
})
if (heap.size() > k)
heap.pop()
if (heap.size() < k)
return None
return heap.peek()
}
```
In questa nuova versione, attraverso la funzione `fold`[^fold], si evita di aggiungere elementi più grandi dell'attuale massimo, che quindi verrano eliminati immediatamente dalla lista per portarla di nuovo ad una lunghezza $k$.
In questo modo l'algoritmo diventa sensibile all'input e nel caso ideale (il più grande elemento tra i primi $k$ elementi dello stream è più piccolo di tutti gli elementi che vengono dopo $k$) si attiene una complessità di $klogk$ e quindi una soluzione che è costante rispetto alla dimensione del problema.
[^fold]: La funzione `fold` ha come parametri due lambda expression, la prima viene eseguita quando l'oggetto è vuoto, mentre la seconda quando l'oggetto esiste.
| best case | average | worst case |
| :-: | :-: | :-: |
| $klogk$ | $nlogk$ | $nlogk$ |
## Source code
- [BinaryHeap](https://git.norangeb.it/norangebit-unisannio-computer-science/lm-tecniche-di-programmazione/src/branch/master/src/main/kotlin/it/norangeb/algorithms/datastructures/queue/priority/BinaryHeap.kt)
- [KMin](https://git.norangeb.it/norangebit-unisannio-computer-science/lm-tecniche-di-programmazione/src/branch/master/src/main/kotlin/it/norangeb/algorithms/exercises/KMin.kt)

View File

@ -102,7 +102,7 @@ class HashTable<K, V> : Dictionary<K, V> {
override fun size(): Int = size
private fun getIndex(key: K): Int {
return key.hashCode() % HASHTABLE_SIZE
return key.hashCode().and(0x7fffffff) % HASHTABLE_SIZE
}
data class Node<K, V>(

View File

@ -27,7 +27,6 @@ package it.norangeb.algorithms.exercises
import arrow.core.None
import arrow.core.Some
import org.amshove.kluent.`should be`
import org.amshove.kluent.shouldEqual
import org.junit.jupiter.api.Test
@ -53,4 +52,25 @@ class KMinTest {
Some(9)
)
}
@Test
fun test5() {
val input = listOf(15, 17, 9, 12, 22, 4, 73, 87, 12, 5)
val kmin = KMin<Int>(5)
val result = input.map { kmin.insert(it) }
result shouldEqual listOf(
None,
None,
None,
None,
Some(22),
Some(17),
Some(17),
Some(17),
Some(15),
Some(12)
)
}
}