2009-12-30 3 views
9

Per la mappa ordinata del clojure, come trovo la voce che ha la chiave più vicina ad un dato valore?Trovare le chiavi più vicine ad un dato valore per le mappe ordinate del clojure

ad es. Supponiamo che io sono

(def my-map (sorted-map 
         1 A 
         2 B 
         5 C)) 

Vorrei una funzione come

(find-closest my-map 4) 

che sarebbe tornato (5, C), dato che è la voce con la chiave più vicina. Potrei fare una ricerca lineare, ma poiché la mappa è ordinata, dovrebbe esserci un modo per trovare questo valore in qualcosa come O (log n).

Non riesco a trovare nulla nell'API che lo rende possibile. Se, ad esempio, potessi chiedere l'entrata della mappa sulla mappa, potrei mettere insieme una funzione come quella che desidero, ma non riesco a trovare nessuna di tali funzioni.

Edit:

Quindi, apparentemente ordinato-mappa è basata su una classe PersistentTreeMap implementato in Java, che è un albero rosso e nero. Quindi sembra proprio che dovrebbe essere fattibile, almeno in linea di principio.

risposta

12

subseq e rsubseq sono molto veloci perché sfruttano la struttura ad albero:

(def m (sorted-map 1 :a, 2 :b, 5 :c)) 

(defn abs [x] (if (neg? x) (- x) x)) 
(defn find-closest [sm k] 
    (if-let [a (key (first (rsubseq sm <= k)))] 
    (if (= a k) 
     a 
     (if-let [b (key (first (subseq sm >= k)))] 
     (if (< (abs (- k b)) (abs (- k a))) 
      b 
      a))) 
    (key (first (subseq sm >= k))))) 

user=> (find-closest m 4) 
5 
user=> (find-closest m 3) 
2 

Questo fa un po 'più di lavoro che ideale, nello scenario ideale avremmo basta fare una < = ricerca poi guardare il nodo il diritto di verificare se c'è qualcosa di più vicino in quella direzione. È possibile accedere all'albero (.tree m) ma i metodi .left e .right non sono pubblici, quindi non è attualmente possibile effettuare il traversal personalizzato.

+0

+1. Grazie, è molto utile. –

0

La prima cosa che mi viene in mente è tirare le chiavi della mappa in un vettore e poi fare una ricerca binaria. Se non c'è corrispondenza esatta con la tua chiave, i due puntatori coinvolti in una ricerca binaria finiranno per indicare i due elementi su entrambi i lati della stessa, e potrai quindi scegliere quella più vicina in una singola operazione (possibilmente in pareggio).

+0

Dal momento che la mappa è già ordinato, I (si spera) non dovrebbe avere a tirare tutte le chiavi della mappa. –

+0

concordato; ma non vedo nessun altro modo per ottenere un accesso casuale alle chiavi. Se fai una ricerca sequenziale, in media devi confrontare il 50% delle chiavi, mentre la mia soluzione richiede la copia al 100% - è orribile - e POUN una ricerca log2 (n). La mia soluzione è buona solo se farai diverse ricerche di questo tipo sugli stessi dati. Forse qualcuno più intelligente apparirà e pubblicherà una soluzione che ci stupirà tutti. –