Algoritmo per generare tutti i set di punti m in reticolo cubico nxnxn che sono unici sotto simmetria


10

Sto implementando un algoritmo che sarà abbastanza complesso dal punto di vista computazionale e voglio provare ad assicurarmi che non sto facendo un lavoro inutile.

Esiste un reticolo cubico nxnxn, ad esempio se n = 2 è costituito da (0,0,0), (0,1,0), (1,0,0), (1,1,0), (0, 1,1), (0,0,1), (1,0,1), (1,1,1).

Da questo reticolo genererò ricorsivamente tutti gli insiemi di punti m, qualcosa del tipo:

solve(set_of_points) {
     if set_of_points.size = m, finish

     do some useful computation here

     for each point in lattice not in set_of_points:
         solve(set_of_points + new_point);
}

Questo può quindi essere chiamato a partire da un set_of_points vuoto.

La natura del problema è tale che in realtà non ho bisogno di ogni permutazione di punti m, ma solo quelli che sono unici sotto le simmetrie naturali del cubo.

Ad esempio, prendi un cubo 2x2x2 e supponiamo di volere tutti i set di 1 punto. Sotto l'algoritmo di base sopra, ci sono 8 diversi set di 1 punto.

Tuttavia, usando le simmetrie del cubo possiamo ridurre questo fino a 1 set unico di 1 punti, poiché tutti gli 8 originali sono equivalenti alle simmetrie del cubo (in questo caso sono tutti "angoli").

Se il cubo è 2x2x2 e m = 2, ci sono 28 insiemi nell'algoritmo di base, ma questo si riduce a solo 3 sotto simmetria (es. {(0,0,0), (1,0,0)}, {(0 , 0,0), (1,1,0)}, {(0,0,0), (1,1,1)})

Ovviamente fare il calcolo su 3 serie di punti è molto meglio di 28, quindi la mia domanda è: come faccio a non generare serie di punti che sono simmetricamente equivalenti a una serie già generata? O se ciò non è possibile, come posso almeno ridurre un po 'il numero di set.

(Nota - se m = 1 questo è relativamente facile - basta scegliere i punti che sono più vicini a (0,0,0) rispetto a qualsiasi altro vertice, con un po 'di confusione ai confini. È per m> 1 che questo ottiene essere un vero problema)


1
In simmetricamente equivalente includi quali operazioni: piani a specchio attraverso il centro? Point-Inversion through center? Tutti e tre i 4 assi di rotazione attraverso il centro?
BmyGuest

Qualsiasi isometria farebbe il trucco
rbennett485,

Se sei ancora in giro, sarebbe consentita la ripetizione nella serie di punti m? Ad esempio, per m = 3, {(0,0,0), (1,1,1), (0,0,0)} è considerato come una selezione valida?
blackpen

@blackpen no, deve essere 3 punti unici
rbennett485

Risposte:


1

Concetto di base:

(1) Possiamo vedere il punto (0,0,0) semplicemente come 000. Ogni punto del reticolo ora cade in una sequenza semplice. Il primo punto è 000, quindi 001, quindi 010 011 100 101 110 e 111. Questo è l'ordine in cui proverai ad aggiungerli al set di punti.

(2) Allo stesso modo, l'insieme {(0,0,0), (0,0,1), (0,1,0)} può essere semplicemente visto come 000001010 e l'insieme {(0,0,0) , (0,1,0), (0,0,1)} possono essere semplicemente visti come 000010001. Due insiemi diversi non possono avere la stessa sequenza e puoi facilmente visualizzare 000001010 come numericamente o alfabeticamente inferiore a 000010001. Chiamiamo questo valore impostato. Ogni possibile set di N punti ora ha un valore impostato e tutti i possibili set di N punti ora rientrano in un semplice elenco ben ordinato.

(3) Ogni gruppo isomorfo di set di punti ha esattamente un membro che avrà il valore impostato più basso. Quelli sono gli unici in cui effettivamente facciamo "calcoli utili".

(4) Ecco la parte che richiederà un lavoro significativo. Prima di eseguire la risoluzione (set_of_points + new_point), si desidera vedere se un isomorfismo abbasserebbe il valore impostato per set_of_points + new_point. Se un isomorfismo abbasserebbe il valore impostato, questo NON è un membro di valore più basso dell'insieme isomorfo. Saltiamo qualsiasi lavoro su questo new_point. Stiamo anche saltando tutto il lavoro ricorsivo che avremmo svolto all'interno di questa soluzione (set_of_points, candidate_point).

solve(set_of_points,new_point) {
 set_of_points = set_of_points + new_point
 do some useful computation here
 if set_of_points.size = m, compute how many isomophisms exist, apply that multiple, finish
 for(candidate_point = new_point+1 to last_point) { /skips point-permutations for free!/
  if ISOMORPH_TESTS_CANNOT_LOWER_VALUE_OF(set_of_points+candidate_point) {
   solve(set_of_points,candidate_point);
  }
 }
}

1

prendendo la notazione della risposta sopra.

consente innanzitutto di definire la simmetria proposta dalla funzione rotate (direction, number_of_time)

soluzione:

(1) crea l'hash di tutti gli insiemi della permutazione con flag = 0 su ciascuno. ad esempio per n = 2, m = 2 000,001 = 0 000,010 = 0 000,011 = 0 ect '...

(2) iniziare da init set, ad esempio i = 000.001

(3) ruota l'insieme i in tutte le direzioni usando la funzione di rotazione (o qualsiasi altra simmetria che ti piace) per esempio, la funzione di rotazione dovrebbe essere chiamata 24 volte per ogni permutazione della rotazione.

inserisci qui la descrizione dell'immagine

spiegazione: qualsiasi numero 1-6 può essere di fronte a te e ciascuno dei numeri può essere ruotato di 4 volte, quindi 6 * 4 = 24

(4) per ogni set riavviato dalla combinazione, imposta la bandiera dell'hash su 1 (ha già un set simmetrico)

(5) aggiorna i al set successivo, ad esempio i = 000,010

(6) se l'insieme i nell'hash è già contrassegnato, vai a (5) altrimenti vai a (3)

abbiamo finito quando tutto l'hash è contrassegnato come 1.


Mi piace molto questo approccio, ma non sarebbe molto utile per il problema originale (non che ti abbia detto di cosa si trattasse!). Il motivo è che questo richiede ancora la generazione di ogni set di punti e il lavoro che ho dovuto fare con ogni set era molto piccolo, quindi questo probabilmente aggiungerebbe tanto sovraccarico quanto risparmiato. Per le applicazioni con un sacco di calcoli da fare per ogni set, questo sarebbe utile però
rbennett485,

1

Nota: penso solo alle simmetrie speculari, non alle simmetrie rotazionali qui.

Supponiamo di avere un (iper) cubo di dimensioni d , ciascuna n unità lunga (un cubo di Rubik sarebbe d = 3, n = 3 ).

Un ingenuo algoritmo genererebbe n ^ d combinazioni di punti e controllerebbe ciascuno per uno scontro di simmetria con tutti gli altri.

Se rappresentiamo una combinazione di punti come vettore bit n ^ d bit, possiamo usare una mappa (vettore bit -> booleano) per contrassegnare tutte le simmetrie del vettore bit con true . Potremmo quindi saltare una combinazione se è già contrassegnata nella mappa.

Questo approccio è molto inefficiente nello spazio: prende una mappa con 2 ^ (n ^ d) voci, cioè una bitmap con così tanti bit. (Per il cubo di Rubik, sarebbe 2 ^ 27 = 128Mbit = 16 Mbytes.)

Possiamo solo ricordare le rappresentazioni canoniche, cioè quei vettori di bit che hanno il valore intero più piccolo, se rappresentato come una parola senza segno n ^ d -bit. Quando generiamo una nuova permutazione di punti, generiamo tutte le sue simmetrie e controlliamo solo se abbiamo visto la simmetria con il valore numerico più piccolo. Questo ci permetterà di memorizzare una mappa con solo 2 ^ n bit (solo 1 byte per il cubo di Rubik), perché abbiamo 2 ^ simmetrie. Ci fa generare queste 2 ^ d simmetrie su ogni passaggio, quindi spendiamo O (2 ^ (d ^ n + d)) = O (2 ^ (d ^ n) * 2 ^ d) tempo. Ancora povero.

Possiamo applicare l'idea del paragrafo precedente al caso monodimensionale. Per generare tutte le combinazioni in un vettore di lunghezza d , possiamo semplicemente incrementare un numero binario d bit di lunghezza, a partire da tutti i 0s. Dividiamo il nostro vettore in due segmenti d / 2- lunghi, ad esempio sinistra e destra. Possiamo notare che per ogni 1bit nel segmento sinistro abbiamo solo bisogno di vedere combinazioni che hanno 1bit nella posizione simmetrica della sezione destra. Altrimenti avremmo già generato una combinazione simmetrica in precedenza, quando le posizioni dei bit sono state scambiate e il 0precedente è arrivato 1. In questo modo, per ogni posizione di bit nella metà destra (r) e la posizione simmetrica nella metà sinistra(l) dobbiamo solo generare 3 combinazioni: (l = 0, r = 0); (l = 1, r = 1); (l = 1, r = 0) . Quindi abbiamo solo bisogno di generare 2 ^ (d / 2) permutazioni di un vettore di lunghezza d , producendo 3 combinazioni per ogni permutazione.

Un cubo di dimensioni d può essere costruito con n ^ (d-1) vettori. Il trucco sopra ci dà i vettori più economici dell'approccio ingenuo. Per generare un cubo, abbiamo bisogno del tempo O (n ^ (d-1) * 2 ^ (d / 2)) .

Se guardiamo il cubo lungo la dimensione dei nostri vettori monodimensionali, possiamo vedere che non dobbiamo controllare la simmetria lungo questa dimensione: durante la generazione dei cubi, eliminiamo le simmetrie per ogni vettore coinvolto.

Ora, se guardiamo attraverso questa dimensione, possiamo riutilizzare lo stesso trucco.

Quando guardiamo attraverso, guardiamo ad esempio i primi frammenti di vettori che formano un piano particolare. Questi bit rappresentano un vettore di bit monodimensionale. Possiamo eliminare la maggior parte delle combinazioni dei suoi bit da motivi di simmetria, come descritto sopra. Quindi, se scegliamo un particolare vettore 1-d di un cubo (ad es. Il più in alto a sinistra), possiamo eliminare molti vettori dello stesso piano (ad esempio il top) in base al valore di un particolare bit. Quindi per un vettore in una posizione simmetrica speculare sul piano, possiamo evitare di generare tutte le combinazioni che potrebbero avere quel bit impostato (o non impostato), riducendo drasticamente il numero di vettori che dobbiamo generare per un determinato piano. Ogni bit eliminato dimezza il numero di possibili vettori nella posizione riflessa dallo specchio. Questo ci dà una sequenza di piani senza controparti simmetriche lungo ogni dimensione.

Questo trucco può essere applicato per limitare ulteriormente la generazione di permutazioni dei seguenti piani lungo una terza dimensione, ecc.

Sebbene non sia un algoritmo completo, spero che questo aiuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.