Algoritmi di programmazione

roberto.p89
ciao a tutti,
non riesco a risolvere questo esercizio di algoritmi:
Sia V [1 : n] un vettore di n valori non negativi. Si progetti un algoritmo che, dato V , due interi k ; j $in$ {1,2...,n}, restituisca i k valori di V piu vicini a V [j]. L'algoritmo deve avere complessita O(n log k). Si fornisca lo pseudocodice dettagliato dell'algoritmo.

La mia soluzione è questa:
- Creo un vettore A [1 : n] dove per ogni indice i da 0 fino a n metto: A = |V [j] - V | (modulo). Così ho la "distanza" tra il valore V [j] ed ogni singolo elemento del vettore.
Questo ha complessità O (n).
- Ora ordino l'array e così avrò dagli indici 2 a k+1 i k valori più vicini a V [j].

Mi sapreste dire con quale metodo di ordinamento riesco ad ottenere una complessità di O(n log k)?
C'è qualcuno che lo risolverebbe in un altro modo?
grazie

Risposte
claudio862
Il tuo metodo ha complessità O(n log n), che è la complessità dell'ordinamento di un array di n elementi.

Potresti usare un red-black tree. Per ogni elemento i dell'array:

    [*:2h3ftr4e]se nell'albero ci sono meno di k elementi lo inserisci (O(log k));[/*:m:2h3ftr4e]
    [*:2h3ftr4e]altrimenti, controlli se l'elemento è maggiore del maggiore elemento nell'albero (O(1) se mantieni un riferimento all'elemento maggiore):

      [*:2h3ftr4e] se sì lo scarti (è meno vicino di tutti gli elementi già nell'albero);[/*:m:2h3ftr4e]
      [*:2h3ftr4e] se no, estrai l'ultimo elemento dall'albero (O(log k)) e inserisci i (O(log k)).[/*:m:2h3ftr4e][/list:u:2h3ftr4e][/*:m:2h3ftr4e][/list:u:2h3ftr4e]

      Le operazioni di inserimento, rimozione e ricerca di un red-black tree di k elementi hanno complessità O(log k). Ripeti su tutti gli elementi dell'array, la complessità finale è O(n log k).

roberto.p89
mmm, non fa parte del programma questo tipo di albero, però questa soluzione è interessante. Praticamente sarebbe un albero AVL in cui inserisco solo k degli n elementi dell'array, giusto?
grazie comunque :)

claudio862
Sì, è un tipo di albero simile ad AVL. L'importante è che sia bilanciato, così che le varie operazioni abbiano costo logaritmico. Se inserisci solo k elementi (se ne inserisci di più, prima togli quelli in eccesso) il numero di elementi nell'albero è sempre minore o uguale a k, quindi le operazioni costano O(log k).
Esistono anche strutture più efficienti, come gli alberi di Van Emde Boas, che hanno complessità O(log log k). Però in realtà il guadagno c'è solo per valori enormi di k.

roberto.p89
ok grazie,
sei stato gentilissimo

apatriarca
Alternativamente, si possono usare i binary heap (un albero di priorità) come segue:
1. Copiare i primi \(k\) indici dell'array in un array ausiliario (\(O(k)\)).
2. Applicare l'algoritmo di costruzione in place di un max-heap all'array ausiliario (\( O(k) \)) usando la distanza tra il valore dell'array corrispondente all'indice e V[j] come "priorità".
3. Per ogni \(i\) da \(k+1\) a \( n \) confrontare la distanza corrispondente all'indice di distanza massima dal max-heap con la distanza tra V e V[j] (\ O(1) \)).
4. Se la distanza tra V e V[j] è minore di quella dell'indice massimo, estrarre l'indice massimo dal max-heap (\( O(\log\,k) \)) e inserire \(i\) nel max-heap (\( O(\log\,k) \)).

In totale è quindi \( O(n\,\log\,k)\) come richiesto... Di fatto non è particolarmente diverso dagli alberi AVL o Red-Black anche se è a mio parere potenzialmente più semplice da implementare e più veloce per \(k\) piccoli (l'albero è interamente contenuto nella cache).

Rispondi
Per rispondere a questa discussione devi prima effettuare il login.