add documentation kmin
All checks were successful
continuous-integration/drone/push Build is passing
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:
parent
6c1bb2bc60
commit
6a554c55c4
79
doc/exercises/kmin.md
Normal file
79
doc/exercises/kmin.md
Normal 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)
|
@ -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>(
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user