2015-03-04 11 views
5

Ho una matrice di dimensione determinata. Voglio attraversarlo è un ordine pseudocasuale, mantenendo la matrice intatta e visitando ogni elemento una volta. Sarà meglio se lo stato corrente può essere memorizzato in pochi interi.Simula l'iterazione casuale dell'array

Lo so you can't have full randomness without storing full array, ma non ho bisogno che l'ordine sia veramente casuale. Ho bisogno che sia percepito come casuale dall'utente. La soluzione dovrebbe utilizzare lo spazio sub-lineare.

Un possibile suggerimento - utilizzando un numero primo grande - è given here. Il problema con questa soluzione è che esiste un passaggio fisso ovvio (dimensione del modulo del modulo preso). Preferirei una soluzione che non sia così ovviamente non casuale. C'è una soluzione migliore?

+1

Sei felice di visitare un determinato elemento più di una volta o no? cioè stai cercando una permutazione casuale degli elementi o una sequenza casuale di elementi? La domanda che stai citando riguarda il primo di questi problemi (noto anche come shuffling). – Walter

+1

Molti (molti?) Generatori di numeri pseudo casuali possono essere definiti da un algoritmo e da un intero o due per descrivere il loro stato corrente. Dati questi fattori, il loro comportamento futuro è del tutto deterministico, anche se apparentemente casuale - questo è ciò che * pseudo * significa qui. Cosa stai cercando non è un PRNG? –

+0

Ovviamente, ho bisogno di evitare ripetizioni (altrimenti posso passare direttamente all'elemento casuale molto tempo). Ho modificato la domanda aggiungendo quell'informazione. Infatti, la soluzione desiderata dovrebbe memorizzare lo stato corrente in un modo che permetta di attraversare N elementi in N passi da qualsiasi stato. – Michal

risposta

2

Come su questo algoritmo?

A pseudo-pseudo attraversano a caso una matrice di dimensione n.

  1. creare una piccola matrice di dimensione k
  2. Utilizzare il metodo di grandi dimensioni numero primo per riempire la piccola matrice, i = 0
  3. rimuovere casualmente un posizione utilizzando un RNG dalla piccola matrice, i + = 1
  4. se i < n - k quindi aggiungere una nuova posizione utilizzando il metodo di grandi dimensioni numero primo
  5. se < n goto 3.

° e più alta è la casualità che ottieni. Questo approccio ti consentirà di ritardare la generazione di numeri dal metodo dei numeri primi.

Un approccio simile può essere fatto per generare un numero prima del previsto nella sequenza creando un altro array, "salta elenco". Seleziona in modo casuale gli elementi più avanti nella sequenza, usali per attraversare la posizione successiva, quindi aggiungili all'elenco dei salti. Quando arrivano in modo naturale vengono ricercati nell'elenco dei salti e soppressi e quindi rimossi dall'elenco salti, a quel punto è possibile aggiungere a caso un altro elemento all'elenco dei salti.

+0

Ho finito con una variazione sul tema skiplist. Tutti gli elementi sono generati dal metodo Numero primi grandi, ma ogni volta che ho bisogno di scegliere un numero, aggiungo prima un numero casuale di elementi alla skiplist (fino a una piccola dimensione fissa). Quindi, scelgo casualmente dalla skiplist o restituisco il numero successivo calcolato con il metodo del numero primo grande (anche questo è deciso a caso). È molto facile da implementare e i numeri sembrano davvero casuali. – Michal

0

Come indicato da altri, è possibile creare una sorta di "piano di volo" in anticipo mescolando una matrice di indici di array e quindi seguirla. Ciò viola il "è meglio se lo stato corrente può essere memorizzato in qualche vincolo di numeri interi" ma è veramente importante? Esistono rigidi vincoli di prestazione? Dopotutto, credo che se non accetti le ripetizioni, devi salvare gli oggetti che hai già visitato da qualche parte o in qualche modo.

In alternativa, si può optare per una soluzione intrusiva e memorizzare un bool all'interno di ogni elemento della matrice, che ti dice se l'elemento è stato già selezionato o meno. Questo può essere fatto in modo quasi pulito utilizzando l'ereditarietà (più se necessario).
Molti problemi vengono con questa soluzione, ad es. sicurezza dei thread e, naturalmente, viola il vincolo "keep the array intact".

+0

E ancora, l'OP non ha chiesto di memorizzare un array di dimensioni simili, o in altre parole - chiede un soluzione in o (n) spazio [piccolo o qui]. – amit

+0

L'ho capito, e in effetti l'ho sottolineato molto chiaramente. Sto proponendo soluzioni parziali e alternative che allentano alcuni vincoli e possono essere probabilmente un buon compromesso. Ad esempio, la soluzione intrusiva può salvare alcuni byte per elemento se paragonata alla mischia di un array di indici. La domanda è: hai letto la risposta? ;) – gd1

+0

Forse questo potrebbe essere combinato con l'uso di un generatore di numeri casuali il cui seme può essere forzato. Il programma selezionerebbe un seme casuale. Ogni volta che sono necessari i dati mescolati, è possibile riprodurli dall'array originale e dal seme. –

0

I residui quadratici che hai menzionato ("utilizzando un grande numero massimo") sono ben noti, funzionano e garantiscono l'iterazione di ogni singolo elemento esattamente una volta (se necessario, ma sembra che non sia rigorosamente il caso?). Sfortunatamente non sono "molto casuali", e ci sono alcuni altri requisiti per il modulo oltre a essere il primo a farlo funzionare.
C'è una pagina sul sito di Jeff Preshing che descrive la tecnica in dettaglio e suggerisce a feed the output of the residue generator into the generator again with a fixed offset.

Tuttavia, dal momento che hai detto che hai semplicemente bisogno di "percepito come casuale dall'utente", sembra che tu possa essere in grado di fare con l'alimentazione di una funzione di hash (ad esempio, cityhash o siphash) con interi consecutivi. L'output sarà un numero intero "casuale" e almeno ci sarà un rigoroso mapping 1: 1 (dal momento che ci sono molti più valori hash possibili di quanti sono gli input).

Ora il problema è che la matrice è più probabile non che di grandi dimensioni, quindi è necessario ridurre in qualche modo la gamma di questi indici generati senza generare duplicati (che è dura).

La soluzione ovvia (prendere il modulo) non funzionerà, in quanto garantisce pressoché di ottenere molti duplicati.

L'utilizzo di una maschera di bit per limitare l'intervallo alla potenza superiore successiva di due dovrebbe funzionare senza introdurre bias e scartare gli indici che sono fuori limite (generando un nuovo indice) dovrebbe funzionare altrettanto bene. Si noti che questo richiede un tempo non deterministico, ma la combinazione di questi due dovrebbe funzionare abbastanza bene (un paio di tentativi al massimo) in media.

Altrimenti, l'unica soluzione che "funziona davvero" è mescolare una serie di indici come indicato da Kamil Kilolajczyk (anche se non lo vuoi).

1

L'idea di un generatore casuale che simula uno shuffle è buona se è possibile ottenerne uno il cui periodo massimo è possibile controllare.

Un Linear Congruential Generator calcola un numero casuale con la formula:

x[i + 1] = (a * x[i] + c) % m; 

La durata massima è m e viene raggiunto quando le seguenti propriet'a:

  • I parametri c e m sono relativamente primi.
  • Per ogni numero primo r dividendo m, un-1 è un multiplo di r.
  • Se m è un multiplo di 4 poi anche un-1 è multiplo di 4.

Il mio primo darft interessate che esprimono m il prossimo multiplo di 4, dopo la lunghezza della matrice e poi trovare adatto a e c valori. Questo è stato (a) un sacco di lavoro e (b) ha prodotto risultati molto ovvi a volte.

Ho ripensato questo approccio. Possiamo fare m il più piccolo potere di due che la lunghezza dell'array si adatterà. L'unico fattore primo di m è quindi 2, che renderà ogni numero dispari relativamente primo ad esso. Con l'eccezione di 1 e 2, m saranno divisibile per 4, che significa che dobbiamo un - 1 A multiplo di 4.

Avendo una maggiore m rispetto alla lunghezza dell'array significa che noi deve scartare tutti i valori che sono indici di array illegali. Ciò avverrà al massimo ogni altro turno e dovrebbe essere trascurabile.

Il seguente codice produce numeri pseudo casuali con un periodo di precisione m. Ho evitato valori insignificanti per a e c e sul mio punto di vista (non troppo numeroso), i risultati sembravano a posto. Almeno non c'era un modello ciclico ovvio.

Quindi:

class RandomIndexer 
{ 
public: 
    RandomIndexer(size_t length) : len(length) 
    { 
     m = 8; 
     while (m < length) m <<= 1; 

     c = m/6 + uniform(5 * m/6); 
     c |= 1; 

     a = m/12 * uniform(m/6); 
     a = 4*a + 1; 
     x = uniform(m);      
    } 

    size_t next() 
    { 
     do { x = (a*x + c) % m; } while (x >= len); 

     return x; 
    } 

private: 
    static size_t uniform(size_t m) 
    { 
     double p = std::rand()/(1.0 + RAND_MAX); 

     return static_cast<int>(m * p); 
    } 

    size_t len; 
    size_t x; 
    size_t a; 
    size_t c; 
    size_t m; 
}; 

È quindi possibile utilizzare il generatore in questo modo:

std::vector<int> list; 
for (size_t i = 0; i < 3; i++) list.push_back(i); 

RandomIndexer ix(list.size()); 
for (size_t i = 0; i < list.size(); i++) { 
    std::cout << list[ix.next()]<< std::endl; 
} 

Sono consapevole che questo ancora non è un grande generatore di numeri casuali, ma è ragionevolmente veloce, non richiede una copia dell'array e sembra funzionare bene.

Se l'approccio di raccogliere un e c casualmente produce cattivi risultati, potrebbe essere una buona idea per limitare il generatore ad alcune potenze di due ed a valori di letteratura hard-code che hanno dimostrato di essere buono.

+0

Sei sicuro che '' 'mentre (p * p Michal

+0

Un altro problema con questa soluzione è che non "sembra" casuale - è facile vedere che c'è un passaggio fisso tra due oggetti successivi. – Michal

+0

Hai ragione. Dobbiamo trovare tutti i fattori. Nei casi in cui _m_ è un prodotto di 4 e un primo, _a_ è effettivamente 1. (È _m_ + 1, ma l'aritmetica del modulo indica che è buono come 1.) Ho corretto il codice. –

0

Ecco una soluzione java, che può essere facilmente convertita in C++ e simile alla soluzione di M Oehm sopra, anche se con un diverso modo di scegliere i parametri LCG.

import java.util.Enumeration; 
    import java.util.Random; 

    public class RandomPermuteIterator implements Enumeration<Long> { 
     int c = 1013904223, a = 1664525; 
     long seed, N, m, next; 
     boolean hasNext = true; 

     public RandomPermuteIterator(long N) throws Exception { 
      if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N); 
      this.N = N; 
      m = (long) Math.pow(2, Math.ceil(Math.log(N)/Math.log(2))); 
      next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE)); 
     } 

     public static void main(String[] args) throws Exception { 
      RandomPermuteIterator r = new RandomPermuteIterator(100); 
      while (r.hasMoreElements()) System.out.print(r.nextElement() + " "); 
      //output:50 52 3 6 45 40 26 49 92 11 80 2 4 19 86 61 65 44 27 62 5 32 82 9 84 35 38 77 72 7 ... 
     } 

     @Override 
     public boolean hasMoreElements() { 
      return hasNext; 
     } 

     @Override 
     public Long nextElement() { 
      next = (a * next + c) % m; 
      while (next >= N) next = (a * next + c) % m; 
      if (next == seed) hasNext = false; 
      return next; 
     } 
    }