2010-11-19 14 views
9

Sto cercando uno spazio di vettori di lunghezza 12, con voci 0, 1, 2. Ad esempio, uno di tali vettori è
001122001122. Ho circa un migliaio di buoni vettori e circa un migliaio di vettori cattivi. Per ogni vettore errato ho bisogno di localizzare il vettore buono più vicino. La distanza tra due vettori è solo il numero di coordinate che non corrispondono. I buoni vettori non sono particolarmente ben disposti, e la ragione per cui sono "buoni" non sembra essere d'aiuto qui. La mia priorità principale è che l'algoritmo sia veloce.Come trovare il vettore più vicino in {0,1,2}^12, più e più volte

Se eseguo una ricerca esaustiva semplice, devo calcolare circa 1000 * 1000 distanze. Sembra abbastanza spessa.

Se applico l'algoritmo di Dijkstra prima utilizzando i buoni vettori, posso calcolare il vettore più vicino e la distanza minima per ogni vettore nello spazio, in modo che ogni vettore errato richieda una ricerca semplice. Ma nello spazio ci sono 3^12 = 531.441 vettori, quindi la precomputazione è di mezzo milione di calcoli a distanza. Non molti risparmi.

Potete aiutarmi a pensare a un modo migliore?

Edit: Dato che la gente ha chiesto ardentemente ciò che li rende "buona": Ogni vettore rappresenta una descrizione di un quadro esagonale di sei triangoli equilateri, che è l'immagine 2D di una disposizione 3D di cubi (si pensi generalizzata Q-bert). I triangoli equilateri sono metà di facce di cubi (45-45-90), inclinati in prospettiva. Sei delle coordinate descrivono la natura del triangolo (pavimento percepito, parete sinistra, parete destra) e sei coordinate descrivono la natura dei bordi (continuità percepita, due tipi di discontinuità percepita). I 1000 buoni vettori sono quelli che rappresentano esagoni che possono essere visti quando si vedono i cubi in prospettiva. La ragione per la ricerca è quello di applicare le correzioni locali ad una mappa esagonale piena di triangoli ...

+3

"La ragione per cui sono 'buoni' non sembra essere utile qui." Se le tue dita non cadono cercando, potrebbe essere utile spiegare cosa rende i vettori 'buoni' e 'cattivi'. Mi è successo molte volte che pensavo che qualcosa fosse inutile e qualcun altro avesse capito come usarlo. – aaronasterling

+1

Trovare distanze 1000 * 1000 in realtà non sembra che richiederebbe molto tempo ... un milione di calcoli di distanza richiederebbe probabilmente un secondo o due anche codificati in un linguaggio di alto livello. – mellamokb

risposta

1

questo suona un po 'come quello che correttori ortografici devono fare. Il trucco è generalmente quello di abusare di tries.

La cosa più semplice che si può fare è costruire un trie sui buoni vettori, quindi eseguire un riempimento a pieno ritmo assegnando priorità ai rami con pochi disallineamenti. Questo sarà molto veloce quando c'è un vettore vicino, e degenererà alla forza bruta quando il vettore più vicino è molto lontano. Non male.

Ma penso che tu possa fare meglio. I vettori non validi che condividono lo stesso prefisso eseguiranno lo stesso lavoro di ramificazione iniziale, quindi possiamo provare a condividerli. Quindi costruiamo anche un trie sui cattivi vettori e lo facciamo in un colpo solo.

garanzie che sia corretto, dal momento che sia l'algoritmo e il codice sono fuori dalla parte superiore della mia testa:

var goodTrie = new Trie(goodVectors) 
var badTrie = new Trie(badVectors) 
var result = new Map<Vector, Vector>() 
var pq = new PriorityQueue(x => x.error) 
pq.add(new {good: goodTrie, bad: badTrie, error: 0}) 
while pq.Count > 0 
    var g,b,e = q.Dequeue() 
    if b.Count == 0: 
     //all leafs of this path have been removed 
     continue 
    if b.IsLeaf: 
     //we have found a mapping with minimum error for this bad item 
     result[b.Item] = g.Item 
     badTrie.remove(b) //prevent redundant results 
    else: 
     //We are zipping down the tries. Branch to all possibilities. 
     q.EnqueueAll(from i in {0,1,2} 
        from j in {0,1,2} 
        select new {good: g[i], bad: b[j], error: e + i==j ? 0 : 1}) 

return result 

Un'ottimizzazione finale potrebbe essere quella di riordinare i vettori in modo da posizioni con alto accordo tra il cattivo i vettori vengono prima e condividono più lavoro.

+0

Interessante. Trie può essere pensato come un automa (dal momento che riconosce una lingua), non sono sicuro dell'algoritmo (perché una coda di priorità?) Ma sembra almeno un buon punto di partenza. Data la dimensione minima dell'alfabeto il Trie dovrebbe essere piuttosto sottile. –

+0

La coda di priorità è necessaria perché si desidera espandere e terminare prima i rami di ricerca a basso errore per eliminare rami di ricerca con errori elevati. –

+0

Anche se non sono sicuro che lo userò, questo è un suggerimento interessante e utile e affronta la domanda di ottimizzazione. Grazie. – Josephine

0

mio geometria computazionale è molto ruvida, ma sembra che si dovrebbe essere in grado di:

  1. calcolare il Voronoi diagram per il tuo set di buoni vettori.
  2. Calcolare lo BSP tree per le celle del diagramma.

Il diagramma di Voronoi ti darà uno scafo convesso di 12 ° dimensione per ogni vettore buono che contiene tutti i punti più vicini a quel vettore.

L'albero BSP vi fornirà un modo veloce per determinare quale cella si trova all'interno di un vettore e, quindi, quale vettore è più vicino.

EDIT: Ho appena notato che si stanno utilizzando distanze di hamming invece di distanze euclidee. Non sono sicuro di come questo possa essere adattato per soddisfare questo limite. Scusate.

4

Solo per mantenere le cose in prospettiva ed essere sicuri di non ottimizzare le cose inutili, l'approccio della forza bruta senza ottimizzazione richiede 12 secondi nella mia macchina.

Codice in Mathematica:

bad = Table[RandomInteger[5, 12], {1000}]; 
good = Table[RandomInteger[2, 12], {1000}]; 
distance[a_, b_] := Total[[email protected][a - b]]; 

bestMatch = #[[2]] & /@ 
    Position[ 
    Table[[email protected] 
     Table[distance[good[[j]], bad[[i]]], {j, [email protected]}], {i, 
     [email protected]}], 1] // Timing 

Come ci si potrebbe aspettare, il tempo segue un O (n^2) legge:

alt text

+0

E se è stato programmato in Java o C#, probabilmente ci vorrebbe solo un secondo o due ... – mellamokb

+0

@mellamokb Sicuro! Questo è il punto. –

1

3^12 non è uno spazio di ricerca molto grande. Se la velocità è essenziale e la generalità dell'algoritmo non lo è, puoi semplicemente associare ciascun vettore a un int nell'intervallo 0..531440 e utilizzarlo come indice in una tabella precompuita di "vettori buoni più vicini".

Se si dà a ciascuna voce di quella tabella una parola a 32 bit (che è più che sufficiente), si dovrebbe considerare circa 2 MB per la tabella, in cambio di un "calcolo" praticamente istantaneo.

modifica: questo non è molto diverso dalla precomputazione suggerita dalla domanda, ma il mio punto è proprio che a seconda dell'applicazione, non c'è necessariamente alcun problema nel farlo in quel modo, specialmente se si fanno tutti i calcoli prima dell'applicazione corre anche.

0

Assumendo una rappresentazione impacchettata per i vettori, un calcolo di distanza (confronto tra un vettore buono e un vettore errato per ottenere la distanza) può essere completato in circa 20 cicli di clock o meno. Quindi un milione di calcoli della distanza può essere fatto in 20 milioni di cicli o (supponendo una CPU a 2 GHz) 0,01 sec. Questi numeri aiutano?

PS: - 20 cicli è una sopravvalutazione prudente.

Problemi correlati