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
|
override fun size(): Int = size
|
||||||
|
|
||||||
private fun getIndex(key: K): Int {
|
private fun getIndex(key: K): Int {
|
||||||
return key.hashCode() % HASHTABLE_SIZE
|
return key.hashCode().and(0x7fffffff) % HASHTABLE_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Node<K, V>(
|
data class Node<K, V>(
|
||||||
|
@ -27,7 +27,6 @@ package it.norangeb.algorithms.exercises
|
|||||||
|
|
||||||
import arrow.core.None
|
import arrow.core.None
|
||||||
import arrow.core.Some
|
import arrow.core.Some
|
||||||
import org.amshove.kluent.`should be`
|
|
||||||
import org.amshove.kluent.shouldEqual
|
import org.amshove.kluent.shouldEqual
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
@ -53,4 +52,25 @@ class KMinTest {
|
|||||||
Some(9)
|
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