Durante la riflessione su un problema, mi sono reso conto che ho bisogno di creare un algoritmo efficiente che risolva il seguente compito:
Il problema: ci viene data una scatola quadrata bidimensionale del lato cui lati sono paralleli agli assi. Possiamo esaminarlo attraverso la cima. Tuttavia, ci sono anche segmenti orizzontali. Ogni segmento ha un numero intero coordinato ( ) e -coordinate ( ) e collega i punti e (guarda il foto sotto).m y 0 ≤ y ≤ n x 0 ≤ x 1 < x 2 ≤ n ( x 1 , y ) ( x 2 , y )
Vorremmo sapere, per ogni segmento di unità nella parte superiore della scatola, quanto possiamo guardare in profondità all'interno della scatola se guardiamo attraverso questo segmento.
Formalmente, per , vorremmo trovare .max i : [ x , x + 1 ] ⊆ [ x 1 , i , x 2 , i ] y i
Esempio: dati e segmenti posizionati come nella figura sotto, il risultato è . Guarda come la luce profonda può entrare nella scatola.
Fortunatamente per noi, sia che sono piuttosto piccoli e possiamo fare i calcoli off-line.
L'algoritmo più semplice per risolvere questo problema è la forza bruta: per ogni segmento attraversare l'intero array e aggiornarlo dove necessario. Tuttavia, ci dà O (mn) non molto impressionante .
Un grande miglioramento consiste nell'utilizzare un albero dei segmenti in grado di massimizzare i valori sul segmento durante la query e di leggere i valori finali. Non lo descriverò ulteriormente, ma vediamo che la complessità temporale è .
Tuttavia, ho trovato un algoritmo più veloce:
Schema:
Ordinare i segmenti in ordine decrescente di coordinate (tempo lineare usando una variazione dell'ordinamento di conteggio). Ora nota che se un segmento di unità è stato coperto da un segmento in precedenza, nessun segmento successivo può più limitare il raggio di luce che attraversa questo segmento di unità . Quindi eseguiremo uno sweep di linea dall'alto verso il basso della casella.x x
Ora introduciamo alcune definizioni: il segmento -unit è un segmento orizzontale immaginario sulla sweep i cui coordinate sono numeri interi e la cui lunghezza è 1. Ogni segmento durante il processo di spazzamento può essere o non contrassegnato (ovvero, un raggio di luce che va dal la parte superiore della scatola può raggiungere questo segmento) o contrassegnata (caso opposto). Si consideri un segmento -Unità con , sempre marcato. Introduciamo anche i set . Ogni set conterrà un'intera sequenza di segmenti di unità contrassegnati consecutivamente (se presenti) con un seguente non contrassegnatox x x 1 = n x 2 = n + 1 S 0 = { 0 } , S 1 = { 1 } , … , S n = { n } x segmento.
Abbiamo bisogno di una struttura di dati in grado di operare su questi segmenti e impostare in modo efficiente. Useremo una struttura find-union estesa da un campo contenente l' indice massimo del segmento -unit (indice del segmento non contrassegnato ).
Ora possiamo gestire i segmenti in modo efficiente. Supponiamo che ora stiamo considerando l' -segmento in ordine (chiamalo "query"), che inizia in e termina in . Dobbiamo trovare tutti i segmenti di unità non contrassegnati che sono contenuti all'interno -segmento (questi sono esattamente i segmenti su cui il raggio luminoso finirà la sua strada). Faremo quanto segue: in primo luogo, troviamo il primo segmento non contrassegnato all'interno della query ( trova il rappresentante del set in cui è contenuto e ottiene l'indice massimo di questo set, che è il segmento non contrassegnato per definizione ). Quindi questo indicex 1 x 2 x i x 1 x y x x + 1 x ≥ x 2 è all'interno della query, aggiungerlo al risultato (il risultato per questo segmento è ) e contrassegnare questo indice ( insiemi di unione contenenti e ). Quindi ripetere questa procedura fino a quando non troviamo tutti i segmenti non contrassegnati , ovvero la successiva ricerca Trova ci fornisce l'indice .
Si noti che ogni operazione di ricerca unione verrà eseguita solo in due casi: o iniziamo a considerare un segmento (che può accadere volte) o abbiamo appena contrassegnato un segmento di unità (ciò può accadere volte). Quindi la complessità complessiva è ( è una funzione inversa di Ackermann ). Se qualcosa non è chiaro, posso approfondire questo aspetto. Forse potrò aggiungere qualche foto se avrò del tempo.
Ora ho raggiunto "il muro". Non riesco a trovare un algoritmo lineare, anche se sembra che dovrebbe essercene uno. Quindi, ho due domande:
- Esiste un algoritmo a tempo lineare (ovvero ) che risolve il problema di visibilità del segmento orizzontale?
- In caso contrario, qual è la prova che il problema di visibilità è ?