2015-04-19 7 views
9

Ho bisogno di determinare se un insieme di punti (ognuno dato da una tupla di float, ognuno dei quali è in [0, 1]) contiene due che sono entro una certa soglia, diciamo 0,01, l'una dall'altra. Dovrei anche menzionare che nella versione del problema che mi interessa, questi "punti" sono dati da tuple di lunghezza ~ 30, cioè sono punti in [0, 1]^30.Come determinare in modo efficiente se un set di punti contiene due che sono chiusi

posso testare se qualsiasi due sono all'interno di questa soglia usando qualcosa come:

def is_near(p1, p2): 
    return sqrt(sum((x1 - x2)**2 for x1, x2 in zip(p1, p2))) < 0.01 # Threshold. 

Usando questo posso solo controllare ogni coppia utilizzando qualcosa di simile:

def contains_near(points): 
    from itertools import combinations 
    return any(is_near(p1, p2) for p1, p2 in combinations(points, r=2)) 

Tuttavia questo è quadratica nel lunghezza della lista, che è troppo lenta per la lunga lista di punti che ho.

Esiste un modo per risolvere questo problema?

Ho provato a fare le cose come scattare questi punti per una griglia in modo da poter utilizzare una mappa dizionario/hash per memorizzare loro:

def contains_near_hash(points): 
    seen = dict() 
    for point in points: 
     # The rescaling constant should be 1/threshold. 
     grid_point = tuple([round(x * 100, 0) for x in point]) 
     if grid_point in seen: 
      for other in seen[grid_point]: 
       if is_near(point, other): 
         return True 
      seen[grid_point].append(point) 
     else: 
      seen[grid_point] = [point] 
    return False 

Tuttavia questo non funziona quando

points = [(0.1149999,), (0.1150001,)] 

Come questi arrotondano a due diversi punti della griglia. Ho anche provato una versione in cui il punto è stato aggiunto a tutti i punti della griglia vicini, tuttavia, poiché gli esempi che voglio fare hanno ~ 30 coordinate, ogni punto della griglia ha 2^30 vicini che lo rendono completamente impraticabile.

+0

L'idea di utilizzare una griglia è corretta e l'approccio comune è quello di utilizzare l'albero R per memorizzare i punti. Inoltre il tuo problema è noto e chiamato "Radios vicini vicini" – ipoteka

+0

Una piccola ottimizzazione: non chiamare 'sqrt()' in 'is_near()', usa invece la distanza al quadrato. –

+0

@ PM2Ring - certo, ma il focus della domanda riguarda davvero il tempo di esecuzione asintotico. –

risposta

6

Una coppia di punti può essere "vicini" solo se la loro distanza in almeno una dimensione è inferiore alla soglia. Questo può essere sfruttato per ridurre il numero di coppie candidate filtrando una dimensione dopo l'altra.

suggerisco:
- ordinare i punti in una dimensione (diciamo: x)
- trova tutti i punti che sono abbastanza vicino al punto successivo nella lista ordinata e mettere il loro indice in una set di candidati
- non utilizzare sqrt() ma la distanza quadratica (x1 - x2)**2 o anche abs(x1 - x2) per l'efficienza
- do che per la seconda dimensione così
- determinare l'intersezione delle due categorie, questi sono punti vicini tra loro

In questo modo, si evitano costose chiamate is_near(), si opera su insiemi più piccoli, si lavora solo con punti univoci e le ricerche di set sono molto efficienti.

Questo schema può essere facilmente esteso per includere più di 2 dimensioni.

+0

FWIW, utilizzo questo approccio in 3D per trovare i colori più vicini per il mapping di palette (in C piuttosto che in Python, quindi non ho codice da condividere). IME funziona bene e mi aspetto che funzioni bene anche per le tuple 30D. –

+0

Sembra un approccio molto interessante (e sostanzialmente diverso). Comunque se lo faccio per la prima, seconda, ... dimensione e ottengo questi elenchi di indici vicini punti non prendo l'intersezione prendi O (n) e quindi finisco con un algoritmo quadratico nel caso peggiore? –

+1

Immagino che il requisito per i punti da chiudere sia ancora più forte: una coppia di punti può essere chiusa solo se la differenza lungo ** tutte ** le dimensioni è più vicina della soglia. Ciò significa che quando si verifica la seconda o la maggiore dimensione, è necessario considerare solo i candidati che sono sopravvissuti al controllo per le dimensioni precedenti. –